* chore(ci): release fires on workflow_dispatch, not on every CI pass
Replaced workflow_run trigger (fired automatically when CI completed
on main) with workflow_dispatch. The full semantic-release automation
is preserved — bump detection, version commit, tag, GitHub Release,
NPM + Docker publish — but now runs only when explicitly triggered.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(transport): add Streamable HTTP mode — one shared process for all MCP clients (v1.2.0)
Add MCP_TRANSPORT=http mode backed by StreamableHTTPServerTransport. Each
Claude Code session connects to the shared long-lived process via HTTP (port
3031 by default) instead of spawning a new stdio process per session, eliminating
per-session process multiplication. Sessions are isolated by mcp-session-id header.
Also refactor src/index.ts to extract createMcpServer()/registerHandlers() for
clean per-session server instantiation, and upgrade fs.writeFileSync/readFileSync
calls to fs.promises async variants in export/import tool handlers.
9 new tests cover transport resolution, session isolation, teardown, and
startMcpHttpServer. All 528 tests pass. Bumps v1.1.0 → v1.2.0.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: newblacc <newblacc@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(canvas): native field preservation, label materialization, batch workspace delete
- fillNativeFields() + repairContainerBinding() in db layer ensure every
element stored in SQLite is a complete, round-trippable Excalidraw element
with correct containerId ↔ boundElements bidirectional binding
- materializeLabel() in server.ts: shapes with label.text/text produce a
native bound text element at write time (create, update, batch) so text
follows its container when moved — matches VSCode Excalidraw extension behavior
- Batch workspace UI: Select/Unselect All, per-row checkboxes, Delete N
workspaces button with confirmation
- POST /api/tenants/batch-delete: delete up to 50 tenants in one request
- 42 new backend tests; 519 total passing
- Bump version 1.0.6 → 1.1.0
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(tests): update e2e assertions for label materialization
phase2-regressions: label is now a native bound text element
(id: pos-stable-1-label) — check bound text element text instead
of container.label?.text which is no longer stored.
sync-flows FTS: search now matches the bound text element (fts-el-label)
rather than the container — accept either id as valid match.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
The file transport defaulted to writing excalidraw.log relative to
process.cwd(), polluting every project directory where the MCP server
started. Console transport already handles warn+error to stderr.
File logging is now opt-in: set LOG_FILE_PATH to enable it.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace Unix-only `2>/dev/null || true` with a `node -e` inline script
that silently ignores better-sqlite3 rebuild failures on all platforms.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@modelcontextprotocol/sdk@1.26.0 uses zod/v4 APIs (specifically
z.literal().value getter). When installed globally, npm shared the
project's zod@3.25.5 instead of the SDK's nested zod@4.3.6; the
3.25.5 v4 compat shim lacks the .value getter, throwing
'Schema method literal must be a string' on startup.
Also updates z.record(z.any()) to z.record(z.string(), z.any())
for zod v4 strict record key typing.
446/446 tests passing.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
ElementSharedFieldsSchema in server.ts was silently stripping textAlign,
verticalAlign, and containerId on every POST/PATCH, causing bound text
inside containers to lose centering after a sync round-trip.
- Add missing fields to ElementSharedFieldsSchema (server.ts)
- Add textAlign?, verticalAlign?, containerId? to ServerElement (types.ts)
- Set textAlign: "center", verticalAlign: "top" on MCP subtitle elements (index.ts)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Block CONNECTING state in WS guard to prevent second connection seeding
knownContainerIdsRef before element_created fires
- Call handleCanvasChange() explicitly after element_created updateScene
(CaptureUpdateAction.NEVER suppresses onChange in Excalidraw 0.18)
- E2E: curved arrow stays deformable after sync round-trip
- E2E: auto-title injection test now reliably passes
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Default font changed to Nunito (id 6)
- Containers auto-inject "Title" + "Text here" subtitle on draw (grouped)
- MCP create_element defaults to title/subtitle card layout for containers
- Sync preserves element geometry (x/y/width/height) across refreshes
- normalizeForBackend preserves native bound text instead of collapsing to label.text
- Rate limits raised to 500 req/15min general, 30 req/min sync writes
- Draggable "Font px" widget with localStorage position persistence
- Left panel widened for full Opacity visibility
- Updated rate-limit tests to match new limits (30 write, 500 general)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
convertToExcalidrawElements silently dropped label.text on reload.
Added expandLabelsToNative() that pre-expands server-format labels
into native Excalidraw bound text elements before the scene is set,
ensuring labels survive DB round-trips.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Update lastSyncedElementsRef on every WS-applied scene change so
auto-sync does not revert MCP writes back to stale browser state
- Fix labeled container updates (rect/ellipse/diamond/arrow) to use
convertToExcalidrawElements with bound-text ID transplant, preventing
text clipping and empty labels after update
- Fix standalone text element updates to write into text/originalText
so Excalidraw renders the new value immediately
- Fix convertTextToLabel to handle arrows and empty string text values
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds postinstall script to rebuild better-sqlite3 native bindings for
the current Node.js version. Fixes ERR_DLOPEN_FAILED when installing
via npx on a different Node version than was used to publish.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- 9 backend security test files (auth, headers, rate-limit, middleware
order, smoke, validation, WS auth, integration bootstrap)
- 1 e2e test (clear-preference)
- SECURITY.md policy doc
These files powered the 369-test suite but were never committed.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- actions/upload-artifact: replace stale v3 SHA (pinned as v4.6.2) with @v4
- docker/metadata-action: replace non-existent SHA 902fa8ec7 with @v5
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>