feat: expand InitializationState with explicit lifecycle state machine#2099
feat: expand InitializationState with explicit lifecycle state machine#2099dgenio wants to merge 5 commits intomodelcontextprotocol:mainfrom
Conversation
Expand the InitializationState enum with new states (Stateless, Closing, Closed, Error) and add centralized transition validation via a _VALID_TRANSITIONS table and _transition_state() method. Key changes: - Add Stateless state for sessions that skip the initialization handshake - Add Closing/Closed states for orderly shutdown tracking - Add Error state for unrecoverable failures with recovery paths - Add _transition_state() method that validates transitions against a table - Add initialization_state property (read-only) and is_initialized property - Override __aexit__ to transition through Closing -> Closed on exit - Update _received_request and _received_notification to use new APIs - Add comprehensive test suite (20 tests) covering all state transitions Github-Issue: modelcontextprotocol#1691
d443932 to
cf1bbd2
Compare
…ition_state Github-Issue: modelcontextprotocol#1691
Add tests for __aexit__ when session is already in Closing or Closed state, covering the 'if' False branch and the second except RuntimeError. Mark the first except RuntimeError as pragma: no cover since it is unreachable under the current transition table (all non-terminal states can transition to Closing).
|
Hi — I'm the author of this PR. Just checking in after @maxisbey's feedback about waiting for transport spec finalization. I designed this as a non-breaking Phase 1 — it only expands the existing enum and adds validated transitions without changing the public API surface. The That said, I completely understand if you'd prefer to wait until the transport discussions linked above converge before merging lifecycle changes. Happy to keep this PR updated in the meantime — it applies cleanly to current Would love to hear if Phase 1 is acceptable as an incremental step, or if you'd rather hold off entirely. Thanks! |
Summary
Addresses #1691 — Phase 1: expand the
InitializationStateenum with an explicit lifecycle state machine and add centralized transition validation.Problem
The current
ServerSessionlifecycle management uses a simple three-state enum (NotInitialized,Initializing,Initialized) with direct state assignment. This makes it difficult to reason about session lifecycle, provides no protection against invalid transitions, and lacks visibility into shutdown states. Stateless sessions were handled by jumping directly toInitialized, conflating two conceptually different modes.Solution
Expanded
InitializationStateenumStateless— sessions that skip the initialization handshake (previously mapped toInitialized)Closing— session is shutting down, entered during__aexit__Closed— terminal state after shutdown completesAn
Errorstate for unrecoverable failures is planned for Phase 2 of #1691.Centralized transition validation
_VALID_TRANSITIONSdict — maps each state to its valid target states, documenting the state machine in one place_transition_state()method — validates transitions and raisesRuntimeErroron invalid ones, with debug loggingInitialized → Initializing) is explicitly allowed for backward compatibilityNotInitialized → Initializedis allowed to handle cases where the client sends anInitializedNotificationwithout a priorInitializeRequestround-tripConcurrency documentation
ServerSessiondocuments the single-consumer concurrency model: incoming messages are processed sequentially by_receive_loop, so_transition_state()needs no external synchronization_transition_state()docstring explains why cooperative scheduling makes the method safe without locksNew properties
initialization_state— read-only access to current stateis_initialized— returnsTruefor bothInitializedandStateless(replaces direct comparisons)__aexit__overrideClosing → Closedon session exitUpdated handlers
_received_requestand_received_notificationnow use_transition_state()instead of direct assignment_received_notificationguards against redundantInitializedNotificationin stateless modeTests
20 new tests in
tests/server/test_session_lifecycle.pycovering:Closedis terminal)_transition_state()is_initializedproperty for all states__aexit__lifecycle transitions (including already-closing and already-closed edge cases)