pub struct SessionState { /* private fields */ }Expand description
Shared session state that can be cloned across requests.
Uses atomic operations for thread-safe state transitions. Includes a type-safe extensions map for storing session-scoped data like authentication claims.
§Example
use tower_mcp::SessionState;
#[derive(Debug, Clone)]
struct UserClaims {
user_id: String,
role: String,
}
let session = SessionState::new();
// Store auth claims in the session
session.insert(UserClaims {
user_id: "user123".to_string(),
role: "admin".to_string(),
});
// Retrieve claims later
if let Some(claims) = session.get::<UserClaims>() {
assert_eq!(claims.role, "admin");
}Implementations§
Source§impl SessionState
impl SessionState
Sourcepub fn insert<T: Send + Sync + Clone + 'static>(&self, val: T)
pub fn insert<T: Send + Sync + Clone + 'static>(&self, val: T)
Insert a value into the session extensions.
This is typically used by auth middleware to store claims that can be checked by capability filters.
§Example
use tower_mcp::SessionState;
let session = SessionState::new();
session.insert(42u32);
assert_eq!(session.get::<u32>(), Some(42));Sourcepub fn get<T: Send + Sync + Clone + 'static>(&self) -> Option<T>
pub fn get<T: Send + Sync + Clone + 'static>(&self) -> Option<T>
Get a cloned value from the session extensions.
Returns None if no value of the given type has been inserted or if
the lock cannot be acquired.
§Example
use tower_mcp::SessionState;
let session = SessionState::new();
session.insert("hello".to_string());
assert_eq!(session.get::<String>(), Some("hello".to_string()));
assert_eq!(session.get::<u32>(), None);Sourcepub fn phase(&self) -> SessionPhase
pub fn phase(&self) -> SessionPhase
Get the current session phase
Sourcepub fn is_initialized(&self) -> bool
pub fn is_initialized(&self) -> bool
Check if the session is initialized (operation phase)
Sourcepub fn mark_handshake_started(&self)
pub fn mark_handshake_started(&self)
Record that an initialize request has been received for this session.
Transports that create a session in response to an initialize frame
call this at the front door, before the request is dispatched. That is
what lets mark_initialized tell the #458
race (the initialized notification overtook a dispatch that is still
running) apart from a client that never sent initialize at all.
mark_initializing also sets this, so a
transport that dispatches initialize in frame order does not need to
call it. Idempotent, and never cleared.
Sourcepub fn handshake_started(&self) -> bool
pub fn handshake_started(&self) -> bool
Whether an initialize request has been received for this session.
False for a fresh session, and for one a client is trying to open with
an unsolicited initialized notification.
Sourcepub fn mark_initializing(&self) -> bool
pub fn mark_initializing(&self) -> bool
Transition from Uninitialized to Initializing.
Called after responding to an initialize request.
Returns true if the transition was successful.
Also records that the handshake has started, so the notification that follows can complete it.
Sourcepub fn mark_initialized(&self) -> bool
pub fn mark_initialized(&self) -> bool
Transition to Initialized phase.
Called when receiving an initialized notification.
Accepts Initializing → Initialized, and Uninitialized → Initialized
only once mark_handshake_started has
run. The latter path handles a race in HTTP transports where the client
sends the initialized notification before the server has finished
processing the initialize request (#458); the handshake flag is what
keeps it from also accepting a client that skipped initialize, which
would leave the server serving a peer whose protocol version and
capabilities it never learned.
Transports that serve without a handshake by design (restored sessions,
optional_sessions, the stateless 2026-07-28 path) want
mark_preinitialized instead.
Returns true if the transition was successful.
Sourcepub fn mark_preinitialized(&self) -> bool
pub fn mark_preinitialized(&self) -> bool
Move the session straight to Initialized, no handshake required.
For transports that serve requests without an initialize exchange
because the protocol or the deployment says they should: a session
restored from a store (it was initialized on another instance), the
optional_sessions opt-in for clients that do not carry a session ID
forward, and the stateless 2026-07-28 path, which has no handshake at
all.
This is a server-side decision, unlike
mark_initialized, which acts on a frame the
client sent. Returns true if the phase changed.
Sourcepub fn is_request_allowed(&self, method: &str) -> bool
pub fn is_request_allowed(&self, method: &str) -> bool
Check if a request method is allowed in the current phase. Per spec:
- Before initialization: only
initializeandpingare valid - During all phases:
pingis always valid
Trait Implementations§
Source§impl Clone for SessionState
impl Clone for SessionState
Source§fn clone(&self) -> SessionState
fn clone(&self) -> SessionState
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read more