You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Authors: Shaun Smith (@evalstate), Kurtis Van Gent (@kurtisvg), Jonathan Hefner (@jonathanhefner) Status: Draft Type: Standards Track Created: 2025-08-19
Abstract
This SEP proposes:
Sessions be elevated from the transport layer (or, more specifically, the Streamable HTTP transport) to the data layer.
Clients may maintain multiple (logical) sessions per server. For example, one session per context window.
Motivation
The current specification for Model Context Protocol (MCP) sessions is ambiguous, leading to confusion and inconsistent implementations across clients and servers. As highlighted in issue #984, the intended purpose and use-case for sessions are not clearly defined, creating a paradox for developers. This proposal aims to resolve this ambiguity by elevating the role of sessions within the MCP, providing a clear and robust framework for stateful interactions.
The Problem with Sessions
The core of the problem lies in the dual and often conflicting roles that sessions are expected to play. Issue #984 categorizes these into two main use-cases:
Transport (or Tracking) Sessions: These sessions are tied to the communication state between a client and a server, including the negotiated protocol version and capabilities.
Logical (or ‘User’ or ‘Contextual’) Sessions: These sessions are intended to group multiple requests within a single logical interaction, as defined by the application (e.g., per LLM context window, per user, or per task).
In addition, we want to solve the following problems highlighted in #984:
Decouple logical sessions from the transport layer session
Provide a clear way to a client to initialize multiple logical sessions per transport session
Allow a server to more clearly indicate if it supports sessions (or not)
Specification
The Transports Working Group is currently debating two alternative designs, and is seeking feedback.
Both designs propose the following changes:
Add an optional sessionId field to each RPC request and response so that they can be associated with a session.
When using the Streamable HTTP transport, require the Mcp-Session-Id HTTP header to mirror the value of the sessionId field.
However, the alternative designs differ in how sessions are started by the client.
Alternative 1: Add a sessions/create Endpoint
Alternative 1 splits sessions into "transport sessions" and "logical sessions". It proposes the following:
Use initialize to mark the start of the transport session. The client should call this method once.
Add a sessions server capability indicating the server's ability to manage logical sessions:
export interface ServerCapabilities {
+ /**+ * If present, the server supports multiple sessions.+ */+ sessions?: {+ /**+ * If `true`, the Server requires valid Session IDs on Requests and+ * Notifications. If `false`, Requests and Notifications can be sent+ * with an empty (null) Session ID.+ */+ required: boolean;+ }
/* ... */
}
Add a sessions/create method, allowing the client to start a logical session after calling initialize. The client may create as many or as few logical sessions as needed.
If the server does not support sessions (i.e., the sessions capability is not present), the client must not call this method.
If the server supports sessions and the sessions.required server capability is true, the client must call this method after initialize, before calling any other methods.
exportinterfaceSessionCreateRequestextendsRequest{method: "session/create";params: {/** * Unique identifier, such as a UUID. * * If omitted, the server will create a new session and return the new * session ID in its response. * * If given, the server will try to update its information about the * client. If the session ID is invalid, the server will return an error. */sessionId?: string;capabilities: ClientCapabilities;};}exportinterfaceSessionCreateResultextendsResult{sessionId: string;}
Remove capabilities: ClientCapabilities from InitializeRequest in favor of adding it to SessionCreateRequest.
sequenceDiagram
participant Client
participant Server
Note over Client,Server: Start transport session
Client->>+Server: InitializeRequest
Server-->>-Client: InitializeResult<br>{ capabilities: { sessions: { required: true } } }
Client->>Server: InitializedNotification
loop
Note over Client,Server: Start logical session
Client->>+Server: SessionCreateRequest<br>{ sessionId: null, params: { capabilities: { ... } } }
Server-->>-Client: SessionCreateResult<br>{ sessionId: "123" }
rect rgb(200, 220, 250)
Note over Client,Server: Session-associated operations
Client->>+Server: ListToolsRequest<br>{ sessionId: "123" }
Server-->>-Client: ListToolsResponse<br>{ sessionId: "123", result: { ... } }
end
opt
Note over Client,Server: End logical session
Client->>+Server: SessionDeleteRequest<br>{ sessionId: "123" }
Server-->>-Client: SessionDeleteResult
end
end
Loading
Alternative 2: Use initialize method for session creation
Alternative 2 has a unified concept of a "data layer sessions". It proposes the following:
Use initialize to the mark the start of a data layer session. The client may call initialize one or more times.
If the server does not use sessions and the client calls initializewithout a sessionId, the server must respond without a sessionId.
If the server does not use sessions and the client calls initializewith a sessionId, the server must respond with an error.
If the server uses sessions and the client calls initializewithout a sessionId, the server must respond with a new sessionId.
If the server uses sessions and the client calls initializewith a sessionId, the server must check whether that sessionId is valid for the client. If the sessionId is valid, the server must respond with that same sessionId. Otherwise, the server must respond with an error.
Summary table:
sessionId from Client
Response from sessionless server
Response from sessionful server
null
null sessionId
new sessionId
valid
error
same sessionId
invalid
error
error
Make notifications/initialized optional, contingent upon whether the protocol version was immediately agreed upon versus negotiated. In other words, the client may skip sending notifications/initialized if the initialize request and response both have the same value for protocolVersion.
Add a terminate method, allowing the client to explicitly end a session.
sequenceDiagram
participant Client
participant Server
loop
Note over Client,Server: Start data layer session
Client->>+Server: InitializeRequest<br>{ sessionId: null, params: { protocolVersion: "456" } }
Server-->>-Client: InitializeResponse<br>{ sessionId: "123", result: { protocolVersion: "456" } }
rect rgb(200, 220, 250)
Note over Client,Server: Session-associated operations
Client->>+Server: ListToolsRequest<br>{ sessionId: "123" }
Server-->>-Client: ListToolsResponse<br>{ sessionId: "123", result: { ... } }
end
opt
Note over Client,Server: End data layer session
Client->>+Server: TerminateRequest<br>{ sessionId: "123" }
Server-->>-Client: TerminateResult
end
end
Loading
Alternative 2 also suggests a future change: an initializeNotRequired: boolean field in server manifests (e.g., server.json). This field would allow a server to declare a priori that it does not depend on initialize-related features such as sessions. If a client knows initializeNotRequired == true, it may skip calling initialize and instead rely on alternative mechanisms, such as reading server capabilities from the manifest.
Rationale
Rationale for Alternative 1: Add a sessions/create Endpoint
Separation of concerns: By separating the session creation from the other aspects of the initialize phase, we don’t need to worry about confusion caused by the re-negotiating other aspects of initialize (such as protocol version or capabilities negotiation)
Indicates sessions are optional: Session creation as a separate RPC call both more clearly communicates that sessions are not mandatory (leaving servers to optionally provide support if warranted) and allows servers to advertise their support for it (or not).
Rationale for Alternative 2: Use initialize method for session creation
Having a unified data-layer session does not require taking a stance on which pieces of state belong to which type of session.
Using initialize means that a session begins as soon as the client and server begin interacting at the data layer. There is no possibility of errant calls between initialize and the start of the session.
Using initialize eliminates a round trip in the case where both the initial handshake and a session are required.
Authors: Shaun Smith (@evalstate), Kurtis Van Gent (@kurtisvg), Jonathan Hefner (@jonathanhefner)
Status: Draft
Type: Standards Track
Created: 2025-08-19
Abstract
This SEP proposes:
Motivation
The current specification for Model Context Protocol (MCP) sessions is ambiguous, leading to confusion and inconsistent implementations across clients and servers. As highlighted in issue #984, the intended purpose and use-case for sessions are not clearly defined, creating a paradox for developers. This proposal aims to resolve this ambiguity by elevating the role of sessions within the MCP, providing a clear and robust framework for stateful interactions.
The Problem with Sessions
The core of the problem lies in the dual and often conflicting roles that sessions are expected to play. Issue #984 categorizes these into two main use-cases:
In addition, we want to solve the following problems highlighted in #984:
Specification
The Transports Working Group is currently debating two alternative designs, and is seeking feedback.
Both designs propose the following changes:
Add an optional
sessionIdfield to each RPC request and response so that they can be associated with a session.export interface Request { method: string; params?: { /* ... */ }; + sessionId?: string; } export interface Notification { method: string; params?: { /* ... */ }; + sessionId?: string; } +export interface Response { + sessionId?: string; +} -export interface JSONRPCResponse { +export interface JSONRPCResponse extends Response { jsonrpc: typeof JSONRPC_VERSION; id: RequestId; result: Result; }When using the Streamable HTTP transport, require the
Mcp-Session-IdHTTP header to mirror the value of thesessionIdfield.However, the alternative designs differ in how sessions are started by the client.
Alternative 1: Add a
sessions/createEndpointAlternative 1 splits sessions into "transport sessions" and "logical sessions". It proposes the following:
Use
initializeto mark the start of the transport session. The client should call this method once.Add a
sessionsserver capability indicating the server's ability to manage logical sessions:export interface ServerCapabilities { + /** + * If present, the server supports multiple sessions. + */ + sessions?: { + /** + * If `true`, the Server requires valid Session IDs on Requests and + * Notifications. If `false`, Requests and Notifications can be sent + * with an empty (null) Session ID. + */ + required: boolean; + } /* ... */ }Add a
sessions/createmethod, allowing the client to start a logical session after callinginitialize. The client may create as many or as few logical sessions as needed.sessionscapability is not present), the client must not call this method.sessions.requiredserver capability istrue, the client must call this method afterinitialize, before calling any other methods.Remove
capabilities: ClientCapabilitiesfromInitializeRequestin favor of adding it toSessionCreateRequest.export interface InitializeRequest extends Request { method: "initialize"; params: { protocolVersion: string; - capabilities: ClientCapabilities; clientInfo: Implementation; }; }Add a
sessions/deletemethod, allowing the client to explicitly end a session.sequenceDiagram participant Client participant Server Note over Client,Server: Start transport session Client->>+Server: InitializeRequest Server-->>-Client: InitializeResult<br>{ capabilities: { sessions: { required: true } } } Client->>Server: InitializedNotification loop Note over Client,Server: Start logical session Client->>+Server: SessionCreateRequest<br>{ sessionId: null, params: { capabilities: { ... } } } Server-->>-Client: SessionCreateResult<br>{ sessionId: "123" } rect rgb(200, 220, 250) Note over Client,Server: Session-associated operations Client->>+Server: ListToolsRequest<br>{ sessionId: "123" } Server-->>-Client: ListToolsResponse<br>{ sessionId: "123", result: { ... } } end opt Note over Client,Server: End logical session Client->>+Server: SessionDeleteRequest<br>{ sessionId: "123" } Server-->>-Client: SessionDeleteResult end endAlternative 2: Use
initializemethod for session creationAlternative 2 has a unified concept of a "data layer sessions". It proposes the following:
Use
initializeto the mark the start of a data layer session. The client may callinitializeone or more times.initializewithout asessionId, the server must respond without asessionId.initializewith asessionId, the server must respond with an error.initializewithout asessionId, the server must respond with a newsessionId.initializewith asessionId, the server must check whether thatsessionIdis valid for the client. If thesessionIdis valid, the server must respond with that samesessionId. Otherwise, the server must respond with an error.Summary table:
sessionIdfrom ClientsessionIdsessionIdsessionIdMake
notifications/initializedoptional, contingent upon whether the protocol version was immediately agreed upon versus negotiated. In other words, the client may skip sendingnotifications/initializedif theinitializerequest and response both have the same value forprotocolVersion.Add a
terminatemethod, allowing the client to explicitly end a session.sequenceDiagram participant Client participant Server loop Note over Client,Server: Start data layer session Client->>+Server: InitializeRequest<br>{ sessionId: null, params: { protocolVersion: "456" } } Server-->>-Client: InitializeResponse<br>{ sessionId: "123", result: { protocolVersion: "456" } } rect rgb(200, 220, 250) Note over Client,Server: Session-associated operations Client->>+Server: ListToolsRequest<br>{ sessionId: "123" } Server-->>-Client: ListToolsResponse<br>{ sessionId: "123", result: { ... } } end opt Note over Client,Server: End data layer session Client->>+Server: TerminateRequest<br>{ sessionId: "123" } Server-->>-Client: TerminateResult end endAlternative 2 also suggests a future change: an
initializeNotRequired: booleanfield in server manifests (e.g.,server.json). This field would allow a server to declare a priori that it does not depend oninitialize-related features such as sessions. If a client knowsinitializeNotRequired == true, it may skip callinginitializeand instead rely on alternative mechanisms, such as reading server capabilities from the manifest.Rationale
Rationale for Alternative 1: Add a
sessions/createEndpointinitializephase, we don’t need to worry about confusion caused by the re-negotiating other aspects of initialize (such as protocol version or capabilities negotiation)Rationale for Alternative 2: Use
initializemethod for session creationinitializemeans that a session begins as soon as the client and server begin interacting at the data layer. There is no possibility of errant calls betweeninitializeand the start of the session.initializeeliminates a round trip in the case where both the initial handshake and a session are required.Backward Compatibility
WIP