diff --git a/docs/docs.json b/docs/docs.json
index 701b79659..7e3e735d3 100644
--- a/docs/docs.json
+++ b/docs/docs.json
@@ -437,6 +437,7 @@
"specification/draft/basic/index",
"specification/draft/basic/lifecycle",
"specification/draft/basic/transports",
+ "specification/draft/basic/streams",
"specification/draft/basic/authorization",
"specification/draft/basic/security_best_practices",
{
diff --git a/docs/specification/draft/basic/lifecycle.mdx b/docs/specification/draft/basic/lifecycle.mdx
index b6a84a3f0..b32ee52b0 100644
--- a/docs/specification/draft/basic/lifecycle.mdx
+++ b/docs/specification/draft/basic/lifecycle.mdx
@@ -155,6 +155,7 @@ Key capabilities include:
| Client | `roots` | Ability to provide filesystem [roots](/specification/draft/client/roots) |
| Client | `sampling` | Support for LLM [sampling](/specification/draft/client/sampling) requests |
| Client | `elicitation` | Support for server [elicitation](/specification/draft/client/elicitation) requests |
+| Client | `streams` | Support for [resumable streams](/specification/draft/basic/streams) |
| Client | `experimental` | Describes support for non-standard experimental features |
| Server | `prompts` | Offers [prompt templates](/specification/draft/server/prompts) |
| Server | `resources` | Provides readable [resources](/specification/draft/server/resources) |
diff --git a/docs/specification/draft/basic/streams.mdx b/docs/specification/draft/basic/streams.mdx
new file mode 100644
index 000000000..33043bc9d
--- /dev/null
+++ b/docs/specification/draft/basic/streams.mdx
@@ -0,0 +1,230 @@
+---
+title: Streams
+---
+
+
+
+**Protocol Revision**: draft
+
+MCP defines a transport-agnostic mechanism for resumable streams that allow clients and servers to maintain state across disconnections. Using resumable streams:
+
+- Clients and servers can disconnect and reconnect without losing progress.
+- Servers can communicate expiration timeouts and reclaim resources thereafter.
+- Clients can poll stream status after disconnect without having to fetch undelivered messages.
+- All of the above works regardless of transport (HTTP, WebSocket, stdio, etc.).
+
+## Lifecycle
+
+A stream follows a defined lifecycle:
+
+1. **Creation**: Server creates a stream and sends a [`notifications/stream/create`][] notification with a unique stream ID.
+2. **Live**: Messages are sent as part of the stream.
+3. **Completion**: Server sends a [`notifications/stream/end`][] notification when the stream is complete.
+
+In the event of disconnection, the client may send a [`stream/poll`][] to check the stream's status, and a [`stream/resume`][] request to receive messages from the stream.
+
+```mermaid
+sequenceDiagram
+ participant Client
+ participant Server
+
+ Client->>+Server: Request (e.g., tools/call)
+ Note over Server: Server creates stream
+ Server-->>Client: notifications/stream/create { streamId: "123", resumeToken: "abc" }
+ loop
+ Server-->>Client: Messages (e.g., notifications/progress)
+ end
+ Server--x-Client: Disconnection occurs
+
+ Note over Client: Client polls stream status (optional)
+ Client->>+Server: stream/poll { streamId: "123", resumeToken: "abc" }
+ Server-->>-Client: StreamPollResult
+
+ Note over Client: Client decides to resume
+ Client->>+Server: stream/resume { streamId: "123", resumeToken: "abc" }
+ Server-->>Client: Undelivered messages
+ loop
+ Server-->>Client: Messages (e.g., notifications/progress)
+ end
+ Server-->>Client: CallToolResult
+
+ Note over Server: Server terminates stream
+ Server-->>-Client: notifications/stream/end { streamId: "123" }
+```
+
+[`notifications/stream/create`]: /specification/draft/schema#notifications%2Fstream%2Fcreate
+[`notifications/stream/end`]: /specification/draft/schema#notifications%2Fstream%2Fend
+[`stream/poll`]: /specification/draft/schema#stream%2Fpoll
+[`stream/resume`]: /specification/draft/schema#stream%2Fresume
+
+### Creation
+
+When a client sends a request that might cause a long-running response, the server **MAY** create a stream by sending a [`notifications/stream/create`][] notification with a unique stream ID, a resume token, and resume interval parameters.
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "notifications/stream/create",
+ "params": {
+ "stream": {
+ "streamId": "550e8400-e29b-41d4-a716-446655440000",
+ "resumeToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
+ "requestId": "1",
+ "resumeInterval": {
+ "min": 5,
+ "max": 3600
+ }
+ }
+ }
+}
+```
+
+- `streamId` and `resumeToken` are used to resume or poll the stream in the event of disconnection.
+- `resumeInterval.min` specifies the minimum seconds a client **SHOULD** wait before resuming or polling the stream in order to prevent excessive reconnections.
+- `resumeInterval.max` specifies the maximum seconds a client may wait before the server considers the stream abandoned.
+
+### Disconnection and Resumption
+
+Either the client or server **MAY** disconnect at any time. After a disconnection, the client can resume the stream by sending a [`stream/resume`][] request with the stream ID and resume token.
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "stream/resume",
+ "params": {
+ "streamId": "550e8400-e29b-41d4-a716-446655440000",
+ "resumeToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
+ }
+}
+```
+
+When a client sends a `stream/resume` request, the server should:
+
+1. Send all undelivered messages for the stream to the client.
+2. If the stream has ended, send a `notifications/stream/end` notification after sending all other undelivered messages.
+3. If the stream ID or resume token is invalid, respond with an error.
+
+```mermaid
+sequenceDiagram
+ participant Client
+ participant Server
+
+ Client->>+Server: Request (e.g., tools/call)
+ Server-->>Client: notifications/stream/create { streamId: "123", resumeToken: "abc" }
+ Server-->>Client: Initial messages
+ Server--x-Client: Disconnection occurs
+
+ Client->>+Server: stream/resume { streamId: "123", resumeToken: "abc" }
+ Server-->>Client: Undelivered messages
+ Server-->>-Client: notifications/stream/end { streamId: "123" }
+```
+
+
+
+When using the [Streamable HTTP transport](/specification/draft/basic/transports#streamable-http) to stream messages, a server cannot definitively know whether a message has been delivered. Clients use the `Last-Event-ID` HTTP header upon reconnect to indicate the last delivered messages.
+
+In order to provide message delivery guarantees, servers may use monotonic SSE event IDs, and maintain a buffer of recently sent messages (a two minute window aligns with common TCP idle timeout configurations). When a client resumes a stream, the `Last-Event-ID` header can be used to determine which messages in the buffer should be discarded before sending the remainder to the client.
+
+
+
+### Polling
+
+Clients can check a stream's status without fetching all undelivered messages by sending a [`stream/poll`][] request with the stream ID and resume token.
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 2,
+ "method": "stream/poll",
+ "params": {
+ "streamId": "550e8400-e29b-41d4-a716-446655440000",
+ "resumeToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
+ }
+}
+```
+
+The response will indicate the status of the stream as well as whether there are pending messages.
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 2,
+ "result": {
+ "streamId": "550e8400-e29b-41d4-a716-446655440000",
+ "status": "live",
+ "pendingMessages": true,
+ "hasRequest": false,
+ "hasError": false
+ }
+}
+```
+
+- `status`: The stream status (`"live"` or `"completed"`)
+- `pendingMessages`: Whether there are pending messages
+- `hasRequest`: Whether any pending messages include a server-to-client request
+- `hasError`: Whether any pending messages include an error result
+
+### Abandonment
+
+If a client does not resume or poll a stream within the `resumeInterval.max` period, the server **MAY** consider the stream abandoned and reclaim resources. The next time the client attempts to resume or poll the stream, the server **MAY** respond with an error.
+
+```mermaid
+sequenceDiagram
+ participant Client
+ participant Server
+
+ Client->>+Server: Request (e.g., tools/call)
+ Server-->>Client: notifications/stream/create { resumeInterval: { max: 3600 } }
+ Server-->>Client: Initial messages
+ Server--x-Client: Disconnection occurs
+
+ Note over Client: Client waits > 3600 seconds
+
+ Client->>+Server: stream/resume
+ Server-->>-Client: Error (stream abandoned)
+```
+
+### Termination
+
+When a stream completes, the server **MUST** send a [`notifications/stream/end`][] notification.
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "notifications/stream/end",
+ "params": {
+ "streamId": "550e8400-e29b-41d4-a716-446655440000"
+ }
+}
+```
+
+## Using Streams with Tool Calls
+
+A common pattern is to use streams with tool calls that might take a long time to complete. When using streams with tool calls:
+
+1. The server sends a `notifications/stream/create` notification immediately after receiving the tool call request.
+2. The server can send progress updates and other messages as part of the stream.
+3. When the tool call completes, the server sends the result.
+4. The server ends the stream with a `notifications/stream/end` notification.
+
+```mermaid
+sequenceDiagram
+ participant Client
+ participant Server
+
+ Client->>+Server: tools/call
+ Server-->>Client: notifications/stream/create { streamId: "123", resumeToken: "abc" }
+
+ loop
+ Server-->>Client: notifications/progress
+ Server-->>Client: Other messages
+ end
+
+ Server-->>Client: CallToolResult
+ Server-->>-Client: notifications/stream/end { streamId: "123" }
+```
+
+## Security Considerations
+
+- Resume tokens should be treated as sensitive information as they can be used to retrieve message history.
+- Servers should validate that clients have appropriate permissions to resume a stream.
diff --git a/docs/specification/draft/basic/transports.mdx b/docs/specification/draft/basic/transports.mdx
index 582976b06..c2302ebe1 100644
--- a/docs/specification/draft/basic/transports.mdx
+++ b/docs/specification/draft/basic/transports.mdx
@@ -106,9 +106,10 @@ MCP endpoint.
- The server **MAY** send JSON-RPC _requests_ and _notifications_ before sending the
JSON-RPC _response_. These messages **SHOULD** relate to the originating client
_request_.
- - The server **SHOULD NOT** close the SSE stream before sending the JSON-RPC _response_
- for the received JSON-RPC _request_, unless the [session](#session-management)
- expires.
+ - The server **SHOULD NOT** close the SSE stream before sending either the JSON-RPC
+ _response_ for the received JSON-RPC _request_ or a
+ [stream creation notification](/specification/draft/basic/streams.mdx#creation),
+ unless the [session](#session-management) expires.
- After the JSON-RPC _response_ has been sent, the server **SHOULD** close the SSE
stream.
- Disconnection **MAY** occur at any time (e.g., due to network conditions).
diff --git a/docs/specification/draft/schema.mdx b/docs/specification/draft/schema.mdx
index 458a2d482..174773f87 100644
--- a/docs/specification/draft/schema.mdx
+++ b/docs/specification/draft/schema.mdx
@@ -27,7 +27,7 @@ the data is entirely optional.
Capabilities a client may support. Known capabilities are defined here, in this schema, but this is not a closed set: any client can define its own, additional capabilities.
Optionalelicitation
elicitation?:object
Present if the client supports elicitation from the server.
Optionalexperimental
experimental?:{[key:string]:object}
Experimental, non-standard capabilities that the client supports.
Optionalroots
roots?:{listChanged?:boolean}
Present if the client supports listing roots.
Type declaration
OptionallistChanged?: boolean
Whether the client supports notifications for changes to the roots list.
Optionalsampling
sampling?:object
Present if the client supports sampling from an LLM.
Capabilities a client may support. Known capabilities are defined here, in this schema, but this is not a closed set: any client can define its own, additional capabilities.
Optionalelicitation
elicitation?:object
Present if the client supports elicitation from the server.
Optionalexperimental
experimental?:{[key:string]:object}
Experimental, non-standard capabilities that the client supports.
Optionalroots
roots?:{listChanged?:boolean}
Present if the client supports listing roots.
Type declaration
OptionallistChanged?: boolean
Whether the client supports notifications for changes to the roots list.
Optionalsampling
sampling?:object
Present if the client supports sampling from an LLM.
elicitation?: object;
experimental?: { [key: string]: object };
roots?: { listChanged?: boolean };
sampling?: object;
}