* 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>
11 KiB
11 KiB
Changelog
All notable changes to this project are documented here. Format: Keep a Changelog
[Unreleased]
[1.1.0] - 2026-04-06
Added
- Batch workspace select/delete UI: Select/Unselect All, per-row checkboxes, Delete N workspaces button with confirmation — active workspace is disabled from selection
POST /api/tenants/batch-deleteendpoint — delete up to 50 tenants in one request with per-tenant cascade (projects, elements, snapshots)fillNativeFields()in db layer — fills all universal and type-specific native Excalidraw fields (angle, strokeColor, roundness, seed, etc.) on every write so elements are identical to those produced by the VSCode Excalidraw extensionrepairContainerBinding()in db layer — enforces bidirectionalcontainerId↔boundElementsbinding on every write path (create, update, batch, sync/v2) so text labels always follow their container when moved- Server-side label materialization (
materializeLabelin server.ts): MCPcreate_element/update_elementcalls withlabel.textortexton a shape now produce a native bound text element in the DB instead of an MCP label stub — no synthetic generation required on export - 42 new backend non-regression tests for native field preservation, container binding repair, and label materialization (519 total)
[1.0.6] - 2026-04-06
Added
DELETE /api/tenants/:idendpoint — delete workspaces (tenants) with cascade (projects, elements, snapshots)- Workspace delete UI: inline confirm buttons in the workspace switcher panel
Fixed
- Project switch in browser did not load new project's elements —
switchProjectUInow directly clears canvas and callsloadExistingElements()instead of relying on WS roundtrip
[1.0.5] - 2026-04-06
Added
- Project management UI in canvas header: create, switch, delete projects with inline confirm
- Sync countdown timer in header — shows seconds until next auto-sync after drawing stops
- REST endpoints:
GET /api/projects,POST /api/projects,PUT /api/project/active,DELETE /api/projects/:id - E2e test suite for project switching round-trips (
project-switch-e2e.test.ts) - Sync countdown unit tests with fake timers (
sync-countdown.test.ts)
Fixed
resolveTenantProjectalways returned first project by creation date instead of the active project — switching projects had no effect on element queriesresolveScopehad the same bug, causing WebSocket broadcasts to target the wrong project- Switching projects while a sync countdown was pending could overwrite the new project with the old project's elements — pending sync now auto-saves before switching
onChangetriggered sync countdown on selection/appState changes (not just element changes) — added element hash comparison to filter false triggers
Changed
- Test count: 477/477 (was 446)
[1.0.3] - 2026-03-30
Fixed
- Global install crash (
Schema method literal must be a string) — upgradedzodfrom3.25.5to^4.3.6so the package uses real zod v4 rather than falling back to zod 3.x's v4 compatibility shim, which lacks the.valuegetter required by@modelcontextprotocol/sdk@1.26.0 z.record(z.any())call updated toz.record(z.string(), z.any())to satisfy zod v4's stricter record key-type requirement
[1.0.2] - 2026-03-30
Added
frontend/src/utils/scenePreparation.ts— centralized scene-preparation utilities (expandLabelsToNative,prepareElementsForScene,convertElementsPreservingImageProps)- E2E regression suite:
tests/e2e/phase2-regressions.spec.ts(4 tests: position stability, auto-title, two-tab sync, curved arrow deformability) - Backend tests:
db-unit,mcp-contract,mcp-sanitization,security-unit,smoke-ws,tenant-authz-behavior - Frontend tests:
scene-preparation,helpers,sync-logic
Changed
computeElementHashis now order-stable for equivalent element setsfrontend/src/App.tsxuses centralized scene-preparation utilities for label expansion and native-vs-converted routing- Rate limits raised: general 100→500 req/15min, write burst 10→30 req/min
- MCP unknown tool calls now return JSON-RPC
MethodNotFound(-32601) instead of generic error - Test count: 446/446
Fixed
- Double WebSocket connection race — second WS created during
CONNECTINGstate seededknownContainerIdsRefprematurely, blocking title auto-injection; guard now also blocksCONNECTINGstate - Title/subtitle not injected for WS-delivered containers —
CaptureUpdateAction.NEVERsuppressesonChange;handleCanvasChange()now called explicitly afterelement_created - Text alignment lost after sync —
ElementSharedFieldsSchemadid not declaretextAlign,verticalAlign,containerId; Zod silently stripped these on every REST round-trip - Curved arrow deforms after sync — element replace strategy discarded Excalidraw-internal control point state; now merges incoming over existing
- MCP
import_sceneprototype pollution —assertNoDangerousKeys()now called on all parsed JSON payloads (MCP stdio bypasses Express middleware) - FTS5 colon column-filter injection —
:added to blocked character set insanitizeSearchQuery - Global state race in
createProject/listProjects— explicittenantId?param added; MCP callers pass captured ID at call time - Subtitle elements in MCP
create_elementnow settextAlign: "center"andverticalAlign: "top" ServerElementtype updated withtextAlign?,verticalAlign?,containerId?pendingTitleTimerRefnow cleaned up on component unmount (prevented stale closure after unmount)localStorageJSON.parse for widget position wrapped in try/catch (malformed value no longer crashes component)- Docker
LABEL org.opencontainers.image.sourcecorrected to fork URL
[1.0.1] - 2026-03-29
Fixed
better-sqlite3native module now rebuilt on install viapostinstallscript, fixing Node.js version mismatch errors (e.g. Node v22 vs v25) when installing via npx
[1.0.0] - 2026-03-29
Changed
- Renamed project from
@sanjibdevnath/mcp-excalidraw-localtoexcalidraw-mcp-sentinel - New npm package name:
excalidraw-mcp-sentinel(unscoped) - GitHub repo:
celstnblacc/excalidraw-mcp-sentinel - Docker images:
celstnblacc/excalidraw-mcp-sentinelandcelstnblacc/excalidraw-mcp-sentinel-canvas - CLI binary renamed:
excalidraw-mcp-sentinel - Version reset to 1.0.0 for independent release track
- Added "Why this fork?" section to README with full attribution
Removed
- Superseded planning docs (PLAN.md, PLAN_v2.md, REVIEW.md, HANDOFF.md)
[1.6.3] - 2026-03-29
Security
- Added
security.tsmiddleware module: helmet headers, explicit CORS allowlist, API key auth with timing-safe comparison, prototype pollution guard, Mermaid input size limits - WebSocket authentication: challenge-response (
auth_required→hello + apiKey) with 5 s timeout and close code 4001 on failure; origin verification viaverifyClient - Rate limiting on all
/api/*routes: 100 req/15 min general, 10 req/min destructive, 10 req/min sync write burst sanitizeSearchQuerynow throws typedInvalidSearchQueryErrorinstead of genericError- Docker: added
deploy.resources.limits(canvas 1 CPU/512M, mcp 0.5 CPU/256M) todocker-compose.yml; extended.dockerignorewithtests/and sensitive key file patterns
Fixed
POST /api/elements/sync: array validation now runs before logger access, preventing aTypeErrorcrash (500) on null/non-array input — now returns 400POST /api/elements/sync/v2: element type validated againstEXCALIDRAW_ELEMENT_TYPESbefore write; invalid types return 400 instead of being persisted silently- Upgraded
zodfrom 3.22.4 to 3.25.5 to resolveERR_PACKAGE_PATH_NOT_EXPORTEDcrash at MCP server startup caused byzod-to-json-schemapeer dependency mismatch
Changed
ElementSharedFieldsSchemaextracted fromCreateElementSchema/UpdateElementSchemato eliminate 25-field duplication; both schemas now use.extend()VALID_ELEMENT_TYPESmoved to module-level constant (was allocated per-request)resolveHelloTenantAndProjectparameter typed asHelloMessage(wasany)getAllFilesObject()helper extracted;sendFilesAdded()andGET /api/filesshare itsendLegacyInitialWsMessagesrenamed tosendAuthlessInitialMessages.project-hooks/pre-commitadded to run vitest on every commit
[Unreleased] - 2026-03-30
Fixed
- Bidirectional sync conflict: WS-applied updates no longer reverted by browser auto-sync (lastSyncedElementsRef now updated on element_updated, element_deleted, elements_batch_created)
- Labeled container updates (rectangle, ellipse, diamond, arrow) now use convertToExcalidrawElements with ID transplant for correct text layout instead of in-place text patch that caused clipping
- Standalone text element updates now write label.text into text/originalText fields so Excalidraw renders the new value
- convertTextToLabel now maps text→label for arrows and empty strings (previously skipped falsy text)
Changed
- Default theme set to dark
Fixed
- Labels stored as label.text (e.g. from MCP updates) now survive page refresh — expandLabelsToNative pre-converts them to bound text before Excalidraw renders
[1.0.4] - 2026-03-31
Fixed
-
npm install -g excalidraw-mcp-sentinelcrashed on Windows —postinstallscript used Unix-only2>/dev/null || truesyntax which cmd.exe does not support; replaced with a cross-platformnode -einline script -
2026-05-14: chore(ci): release workflow now manual (workflow_dispatch) -- no longer fires automatically on every CI pass on main
[1.2.0] - 2026-05-20
Added
- Streamable HTTP transport mode (
MCP_TRANSPORT=http): single long-lived process serves all MCP clients over HTTP instead of spawning a new stdio process per session. Each client gets its own isolatedServerinstance routed bymcp-session-idheader. Eliminates per-session process overhead for multi-session setups. src/mcp-http.ts:mountMcpRoutes,startMcpHttpServer,resolveTransportMode— full HTTP session lifecycle (POST/GET/DELETE /mcp, session map,StreamableHTTPServerTransport)createMcpServer()factory andregisterHandlers()insrc/index.ts— clean per-session server instantiation for HTTP mode- 9 new tests in
tests/backend/mcp-http.test.tscovering transport resolution, session isolation, session teardown, andstartMcpHttpServer CLAUDE.md: Strict Installation Decoupling rule- launchd agent (
~/Library/LaunchAgents/com.user.excalidraw-mcp.plist) for single-instance persistence on macOS
Changed
runServer()now checksMCP_TRANSPORTenv var; defaults to stdio (backward-compatible)fs.writeFileSync/readFileSynccalls in export/import tool handlers converted tofs.promisesasync variants