Skip to content

MCP stdio edits to a project open in the editor are lost on its next autosave #1138

Closed

Description

@EtienneLescot

Summary

An edit sent through the stdio MCP server (Openscreen.exe mcp) to a project that is open in the GUI editor is accepted, written to disk, then silently overwritten by the editor's next autosave. The agent is told the edit succeeded; it is gone. P1, silent data loss.

Repro

Measured on the CI build of release/v2.1.0 @ 9c55596, Windows 11:

  1. Open project P in the GUI editor.
  2. From a stdio MCP client (official MCP SDK client): addTrim {projectId: P, ...} is refused with "not open in the editor ... call createCheckpoint first", so the server believes P is closed. listProjects reports P with open: false.
  3. createCheckpoint {projectId: P}, then addTrim: accepted, project file rewritten at 17:07:25 with the trim.
  4. Change anything in the editor (e.g. background animation): autosave at 17:07:44 rewrites the file without the MCP trim.

The checklist requires the opposite (manual-e2e-checklist.md, MCP server — post-2.0.0): "Open the project in the editor, then send an MCP edit to it as a closed project, and confirm it is refused rather than overwriting the editor's copy."

Root cause

  • openscreen mcp is a separate process. CLI runs skip the single-instance lock (electron/main.ts, hasSingleInstanceLock) so they can run beside the app.
  • The MCP server learns which project is open only from McpDocumentHost.snapshot(), that is from an editor window in its own process (EditorDocumentHost). The stdio process has no editor, so every project looks closed: listProjects says open: false and edits take the file path (runOnFile).
  • DocumentService.saveProjectIfUnchanged guards against an editor that opened the project mid-call only through an in-process read generation plus a compare of the file content. The GUI read the project before the call, in another process, so neither check sees it.
  • The editor then saves its own copy with saveProject, which does not look at the file. The external edit is lost without a word.

The in-app HTTP server is not affected: it lives in the GUI process and asks the editor.

Proposed fix

  • The GUI main process records which project its editor holds in a small marker file beside the projects (<projects>/.open-in-editor.json, { pid, projectId }), written when the editor loads or switches project and removed when it closes the project or its window goes away.
  • DocumentService.projectOpenInEditor() reads it. A marker whose pid is no longer alive is ignored, so a crashed GUI never blocks edits; the next GUI replaces it.
  • The MCP server uses it in both processes: listProjects reports open truthfully across processes, and a write to a project open in another process's editor is refused with a message that says to close it there or use the in-app MCP server.
  • saveProjectIfUnchanged, which every external writer goes through (the document tools and setPointerTrack), checks the marker again inside the project's write queue just before writing, so the guard does not depend on the tool entry.

Editor-side guard: not in this fix

The editor could also refuse to save over a file someone else changed. Not done here:

  • With the marker in place, no OpenScreen writer edits a project the editor holds, so there is no external write left for it to catch, apart from the race below.
  • Detecting it is the easy half. The hard half is what to do next (reload, merge, or ask the user), which needs UX of its own and is out of proportion for a release-candidate fix.

Known ceilings:

  • An editor that reads the project in the few milliseconds between the marker check and the write can still hold a copy without that edit.
  • A pid reused after a GUI crash can make a project look open until the GUI's editor runs again. That fails toward a refusal, never toward a silent overwrite.

Activity

  1. EtienneLescot commented on Oct 12, 2026

    @EtienneLescot
    CollaboratorAuthor

    Also reproduced on macOS 26.5 arm64, same candidate (release/v2.1.0 @ 9c55596, build run 38252812147), e2e results in #1131:

    • Pass 1: with the editor showing proj_4a7d8831 ("E2E speech fixture"), the stdio server's listProjects reported open: false for it. An addTrim to it was refused only because no checkpoint existed ("this project is not open in the editor ... Call createCheckpoint"), not because the editor held it.
    • Pass 2: after createCheckpoint, the same kind of edit was accepted (success returned to the agent), then lost when the editor saved its own copy.

    So this is not Windows-specific: the stdio process has no editor of its own on any platform. #1139 was a duplicate of this one and is closed.

  2. added
    status: fixed in mainWork is merged into main but may not be in a downloadable release yet.
    status: pending releaseMerged change is waiting for a packaged desktop release.
    on Oct 13, 2026
  3. added this to the Next Release milestone on Oct 13, 2026
  4. github-actions commented on Oct 13, 2026

    @github-actions
    Contributor

    Fixed by #1154 and merged into main.

    This change is assigned to the Next Release release milestone and is not necessarily available in the latest downloadable desktop release yet. It is currently marked as status: pending release until a packaged release containing it is published.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingstatus: fixed in mainWork is merged into main but may not be in a downloadable release yet.status: pending releaseMerged change is waiting for a packaged desktop release.

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions