Skip to content

[Server] Save the session writes of a handler after it resumed - #598

Merged
chr-hertel merged 2 commits into
modelcontextprotocol:mainfrom
chr-hertel:fix-save-session-after-resume
Oct 14, 2026
Merged

chr-hertel merged 2 commits into
modelcontextprotocol:mainfrom
chr-hertel:fix-save-session-after-resume

Conversation

@chr-hertel

Copy link
Copy Markdown
Member

Stacked on #584 - only the last commit belongs here; rebased once #584 is.

A handler that writes to its session after it resumed from an elicitation, sampling request or notification loses those writes: nothing saves the fiber's session once it terminates.

  • Once a fiber that suspended terminates, Protocol writes the keys the handler changed since it suspended onto the stored session, under the session lock
  • Only the changed keys, not the whole fiber session: that one still holds the consumed answer, the drained outgoing queue and whatever other requests saved since
  • A busy lock or a session destroyed in the meantime skips the save, the response still goes out

Fixes chr-hertel#85

🤖 Generated with Claude Code

@chr-hertel chr-hertel added this to the 0.9.0 milestone Oct 13, 2026
@chr-hertel chr-hertel added bug Something isn't working Server Issues & PRs related to the Server component labels Oct 13, 2026
@chr-hertel
chr-hertel requested a balanced review from Copilot October 13, 2026 23:46

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Failed writes remain undetected in key paths, and timed-out teardown can allow a destroyed session to be recreated.

6 open findings
What changed in this PR

Adds opt-in per-session locking and preserves session changes made by resumed handlers.

Changes:

  • Adds file-based and Symfony-backed session locks.
  • Serializes session access and merges resumed-handler changes.
  • Adds documentation and extensive concurrency tests.
File Description
CHANGELOG.md Records locking and resumed-session behavior.
composer.json Adds optional Symfony Lock support.
docs/​run/​sessions.md Documents concurrent session handling.
docs/​run/​server-builder.md Documents the builder lock option.
src/​Exception/​SessionLockException.php Defines lock failures.
src/​Server/​Builder.php Configures session locks.
src/​Server/​ClientGateway.php Propagates notification queue failures.
src/​Server/​Protocol.php Implements locking and resumed-session merging.
src/​Server/​Session/​FileSessionLock.php Implements filesystem locks.
src/​Server/​Session/​FileSessionStore.php Collects expired lock files.
src/​Server/​Session/​SessionLockInterface.php Defines the locking contract.
src/​Server/​Session/​SymfonyLockSessionLock.php Implements distributed locks.
src/​Server/​Transport/​BaseTransport.php Resumes fibers after yield failures.
tests/​Unit/​Server/​BuilderTest.php Tests builder integration.
tests/​Unit/​Server/​ClientGatewayTest.php Tests notification failures.
tests/​Unit/​Server/​ProtocolTest.php Tests resumed-session persistence.
tests/​Unit/​Server/​ProtocolSessionLockTest.php Tests protocol locking behavior.
tests/​Unit/​Server/​Session/​FileSessionLockTest.php Tests filesystem locking.
tests/​Unit/​Server/​Session/​FileSessionStoreTest.php Tests lock-file collection.
tests/​Unit/​Server/​Session/​SymfonyLockSessionLockTest.php Tests distributed locking.
tests/​Unit/​Server/​Session/​Fixture/​HeldElsewhereSessionLock.php Simulates lock contention.
tests/​Unit/​Server/​Session/​Fixture/​RecordingSessionLock.php Records lock operations.
tests/​Unit/​Server/​Session/​Fixture/​RecordingSessionStore.php Records store operations.
tests/​Unit/​Server/​Transport/​StreamableHttpTransportTest.php Tests HTTP concurrency flows.

🧠 Review effort: Balanced


💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/Server/Protocol.php Outdated
Comment thread src/Server/Protocol.php Outdated
Comment thread src/Server/Protocol.php Outdated
Comment on lines +236 to +237
if ($takesLock && !$this->acquireSessionLock($sessionId)) {
$this->sendResponse($transport, Error::forServerError(self::SESSION_BUSY_MESSAGE, self::findResponseId($input)), null, ['status_code' => 503]);
Comment thread src/Server/Protocol.php Outdated
Comment thread src/Server/Protocol.php Outdated
Comment thread src/Server/Session/SessionLockInterface.php Outdated
@chr-hertel
chr-hertel force-pushed the fix-save-session-after-resume branch from 79326f1 to 80dc6c2 Compare October 14, 2026 22:36
@chr-hertel
chr-hertel requested a balanced review from Copilot October 14, 2026 22:39
@chr-hertel
chr-hertel marked this pull request as ready for review October 14, 2026 22:39

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment thread src/Server/Protocol.php Outdated
Comment thread src/Server/Protocol.php Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@chr-hertel
chr-hertel merged commit b516cf3 into modelcontextprotocol:main Oct 14, 2026
28 checks passed
@chr-hertel
chr-hertel deleted the fix-save-session-after-resume branch October 14, 2026 23:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working Server Issues & PRs related to the Server component

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Server] Session writes of a handler after it resumed are lost

2 participants