fix(mcp): report skipped persistence instead of silently dropping live sync - #736
Merged
Conversation
…e sync publishLiveSceneSnapshot returned void and silently skipped both the draft save and the live event whenever no active scene was bound or the store could not append scene events — mutations 'vanished' with no error, no log, and no signal to the caller, sending people hunting in the wrong layer (#725). Neither of the two fixes #561 debated survives the constraints: a typed throw breaks the plain --stdio quick start on the first create_wall, and lazily binding a draft scene creates persistent artifacts the user never asked for on stores that may require project context. Instead the skip becomes visible at the layer the caller sees: publishLiveSceneSnapshot returns 'published' | 'unbound' | 'events_unsupported', and every mutating tool spreads a persistence warning into its result (declared in the tool outputSchema via a shared fragment so the SDK's structured- content validation keeps it). An AI caller can react by binding a scene with save_scene/load_scene; a human reading the transcript sees why nothing persisted. InMemorySceneStore now implements appendSceneEvent/listSceneEvents so the three paths are testable; live-sync.test.ts covers them through a real client/server pair and at the unit level. Fixes #725 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What does this PR do?
Fixes #725. When a store was attached but no scene was bound (or the store couldn't append scene events),
publishLiveSceneSnapshotreturned silently: the mutation applied to the in-memory session, but nothing persisted, no live event reached subscribers, and nothing told the caller — the failure mode that sent #561's investigation into the wrong layer.Design decision (the question #561 left open): neither throw nor lazily bind. A typed
no_active_scenethrow breaks the README--stdioquick start on the firstcreate_wall; lazy draft binding creates persistent artifacts the user never asked for, on stores that may require project context. Instead the skip becomes visible:publishLiveSceneSnapshotnow returns'published' | 'unbound' | 'events_unsupported'(built on the existingcanAppendSceneEventsseam).apply_patch,create_wall,create_room,add_door,add_window,furnish_room,cut_opening,create_story_shell,create_roof,create_stair_between_levels,create_level,duplicate_level,place_item,delete_node,set_zone,undo,redo) spreads apersistence: { status, warning }field into its result when the change stayed in-memory — declared in each tool'soutputSchemavia a sharedliveSyncOutputfragment so the SDK's structured-content validation keeps it. The warning tells the caller to bind a scene withsave_sceneorload_scene.undo/redo/furnish_roomkeep their conditional publish: no mutation → no warning.InMemorySceneStore(test-utils) now implementsappendSceneEvent/listSceneEvents, which MCP live-sync silently drops mutations when no active scene is bound #725 called out as the missing test seam.How to test
bun test packages/mcp/src/tools/live-sync.test.ts— six tests: unbound / published / events-unsupported, each through a realInMemoryTransportclient-server pair, plus the three statuses at the unit level.bun test packages/mcp/src/tools— full tool suite (190 tests) passes.pascal-mcp --stdio, callcreate_wallwithout opening a scene — the result now carriespersistence.status: "unbound"with the guidance string instead of silently not persisting.Screenshots / screen recording
N/A — MCP server change, no UI.
Checklist
bun devbun checkto verify)mainbranchNote
Medium Risk
Touches every mutating MCP tool response shape and persistence behavior; callers may rely on the new optional
persistencefield, but successful paths stay backward compatible.Overview
MCP scene mutations no longer fail silently when live sync cannot persist.
publishLiveSceneSnapshotreturns'published' | 'unbound' | 'events_unsupported'instead of no-oping without feedback when there is no bound scene or the store cannot append scene events.All mutating tools wire this through shared
liveSyncOutput(output schema) andpersistencePayload: successful publishes add nothing; skipped sync adds optionalpersistence: { status, warning }with guidance to bind viasave_scene/load_scene.undo,redo, andfurnish_roomonly publish (and warn) when they actually change the graph.Tests cover the three statuses via
create_wallover in-memory transport and extendInMemorySceneStorewithappendSceneEvent/listSceneEventsfor the live-sync path.Reviewed by Cursor Bugbot for commit 6bd49f3. Bugbot is set up for automated code reviews on this repo. Configure here.