diff --git a/docs/specification/draft/basic/_index.md b/docs/specification/draft/basic/_index.md index b05c729a..d4fb21f2 100644 --- a/docs/specification/draft/basic/_index.md +++ b/docs/specification/draft/basic/_index.md @@ -54,7 +54,7 @@ See the following pages for more details on the different components: ## Auth MCP provides an [Authorization]({{< ref "/specification/draft/basic/authorization" >}}) -framework for HTTP+SSE transport. Implementations using HTTP+SSE transport **SHOULD** +framework for use with HTTP. Implementations using an HTTP-based transport **SHOULD** conform to this specification, whereas implementations using STDIO transport **SHOULD NOT** follow this specification, and instead retrieve credentials from the environment. diff --git a/docs/specification/draft/basic/authorization.md b/docs/specification/draft/basic/authorization.md index 97b1dde6..76c43302 100644 --- a/docs/specification/draft/basic/authorization.md +++ b/docs/specification/draft/basic/authorization.md @@ -12,13 +12,13 @@ weight: 15 The Model Context Protocol provides authorization capabilities at the transport level, enabling MCP clients to make requests to restricted MCP servers on behalf of resource -owners. This specification defines the authorization flow for HTTP+SSE transport. +owners. This specification defines the authorization flow for HTTP-based transports. ### 1.2 Protocol Requirements Authorization is **OPTIONAL** for MCP implementations. When supported: -- Implementations using an HTTP+SSE transport **SHOULD** conform to this specification. +- Implementations using an HTTP-based transport **SHOULD** conform to this specification. - Implementations using an STDIO transport **SHOULD NOT** follow this specification, and instead retrieve credentials from the environment. - Implementations using alternative transports **MUST** follow established security best @@ -120,19 +120,17 @@ For example: `MCP-Protocol-Version: 2024-11-05` #### 2.3.2 Authorization Base URL -The authorization base URL **MUST** be determined from the [SSE -endpoint]({{< ref "specification/draft/basic/transports#http-with-sse" >}}) URL by -discarding any existing `path` component. For example: +The authorization base URL **MUST** be determined from the MCP server URL by discarding +any existing `path` component. For example: -If the SSE endpoint is `https://api.example.com/v1/sse`, then: +If the MCP server URL is `https://api.example.com/v1/mcp`, then: - The authorization base URL is `https://api.example.com` - The metadata endpoint **MUST** be at `https://api.example.com/.well-known/oauth-authorization-server` This ensures authorization endpoints are consistently located at the root level of the -domain serving the SSE endpoint, regardless of any path components in the SSE endpoint -URL. +domain hosting the MCP server, regardless of any path components in the MCP server URL. #### 2.3.3 Fallbacks for Servers without Metadata Discovery @@ -147,7 +145,7 @@ For servers that do not implement OAuth 2.0 Authorization Server Metadata, clien | Token Endpoint | /token | Used for token exchange & refresh | | Registration Endpoint | /register | Used for dynamic client registration | -For example, with an SSE endpoint of `https://api.example.com/v1/sse`, the default +For example, with an MCP server hosted at `https://api.example.com/v1/mcp`, the default endpoints would be: - `https://api.example.com/authorize` @@ -253,6 +251,9 @@ requirements for resource requests. Specifically: Authorization: Bearer <access-token> ``` +Note that authorization **MUST** be included in every HTTP request from client to server, +even if they are part of the same logical session. + 2. Access tokens **MUST NOT** be included in the URI query string Example request: diff --git a/docs/specification/draft/basic/transports.md b/docs/specification/draft/basic/transports.md index 739b77a8..0f9c7d78 100644 --- a/docs/specification/draft/basic/transports.md +++ b/docs/specification/draft/basic/transports.md @@ -12,7 +12,7 @@ The protocol currently defines two standard transport mechanisms for client-serv communication: 1. [stdio](#stdio), communication over standard in and standard out -2. [HTTP with Server-Sent Events](#http-with-sse) (SSE) +2. [Streamable HTTP](#streamable-http) Clients **SHOULD** support stdio whenever possible. @@ -48,38 +48,210 @@ sequenceDiagram deactivate Server Process ``` -## HTTP with SSE +## Streamable HTTP -In the **SSE** transport, the server operates as an independent process that can handle -multiple client connections. +{{< callout type="info" >}} This replaces the [HTTP+SSE +transport]({{< ref "/specification/2024-11-05/basic/transports#http-with-sse" >}}) from +protocol version 2024-11-05. See the [backwards compatibility](#backwards-compatibility) +guide below. {{< /callout >}} -The server **MUST** provide two endpoints: +In the **Streamable HTTP** transport, the server operates as an independent process that +can handle multiple client connections. This transport uses HTTP POST and GET requests. +Server can optionally make use of +[Server-Sent Events](https://en.wikipedia.org/wiki/Server-sent_events) (SSE) to stream +multiple server messages. This permits basic MCP servers, as well as more feature-rich +servers supporting streaming and server-to-client notifications and requests. -1. An SSE endpoint, for clients to establish a connection and receive messages from the - server -2. A regular HTTP POST endpoint for clients to send messages to the server +The server **MUST** provide a single HTTP endpoint path (hereafter referred to as the +**MCP endpoint**) that supports both POST and GET methods. For example, this could be a +URL like `https://example.com/mcp`. -When a client connects, the server **MUST** send an `endpoint` event containing a URI for -the client to use for sending messages. All subsequent client messages **MUST** be sent -as HTTP POST requests to this endpoint. +### Message Exchange -Server messages are sent as SSE `message` events, with the message content encoded as -JSON in the event data. +1. Every JSON-RPC message sent from the client **MUST** be a new HTTP POST request to the + MCP endpoint. + +2. When the client sends a JSON-RPC _request_ to the MCP endpoint via POST: + + - The client **MUST** include an `Accept` header, listing both `application/json` and + `text/event-stream` as supported content types. + - The server **MUST** either return `Content-Type: text/event-stream`, to initiate an + SSE stream, or `Content-Type: application/json`, to return a single JSON-RPC + _response_. The client **MUST** support both these cases. + - If the server initiates an SSE stream: + - The SSE stream **SHOULD** eventually include a JSON-RPC _response_ message. + - The server **MAY** send JSON-RPC _requests_ and _notifications_ before sending a + 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_, unless the [session](#session-management) expires. + - After the JSON-RPC _response_ has been sent, the server **MAY** close the SSE + stream at any time. + - Disconnection **MAY** occur at any time (e.g., due to network conditions). + Therefore: + - Disconnection **SHOULD NOT** be interpreted as the client cancelling its + request. + - To cancel, the client **SHOULD** explicitly send an MCP `CancelledNotification`. + - To avoid message loss due to disconnection, the server **MAY** make the stream + [resumable](#resumability-and-redelivery). + +3. When the client sends a JSON-RPC _notification_ or _response_ to the MCP endpoint via + POST: + + - If the server accepts the message, it **MUST** return HTTP status code 202 Accepted + with no body. + - If the server cannot accept the message, it **MUST** return an HTTP error status + code (e.g., 400 Bad Request). The HTTP response body **MAY** comprise a JSON-RPC + _error response_ that has no `id`. + +4. The client **MAY** also issue an HTTP GET to the MCP endpoint. This can be used to + open an SSE stream, allowing the server to communicate to the client without the + client first sending a JSON-RPC _request_. + - The client **MUST** include an `Accept` header, listing `text/event-stream` as a + supported content type. + - The server **MUST** either return `Content-Type: text/event-stream` in response to + this HTTP GET, or else return HTTP 405 Method Not Allowed, indicating that the + server does not offer an SSE stream at this endpoint. + - If the server initiates an SSE stream: + - The server **MAY** send JSON-RPC _requests_ and _notifications_ on the stream. + These messages **SHOULD** be unrelated to any concurrently-running JSON-RPC + _request_ from the client. + - The server **MUST NOT** send a JSON-RPC _response_ on the stream **unless** + [resuming](#resumability-and-redelivery) a stream associated with a previous + client request. + - The server **MAY** close the SSE stream at any time. + - The client **MAY** close the SSE stream at any time. + +### Multiple Connections + +1. The client **MAY** remain connected to multiple SSE streams simultaneously. +2. The server **MUST** send each of its JSON-RPC messages on only one of the connected + streams; that is, it **MUST NOT** broadcast the same message across multiple streams. + - The risk of message loss **MAY** be mitigated by making the stream + [resumable](#resumability-and-redelivery). + +### Resumability and Redelivery + +To support resuming broken connections, and redelivering messages that might otherwise be +lost: + +1. Servers **MAY** attach an `id` field to their SSE events, as described in the + [SSE standard](https://html.spec.whatwg.org/multipage/server-sent-events.html#event-stream-interpretation). + - If present, the ID **MUST** be globally unique across all streams within that + [session](#session-management)—or all streams with that specific client, if session + management is not in use. +2. If the client wishes to resume after a broken connection, it **SHOULD** issue an HTTP + GET to the MCP endpoint, and include the + [`Last-Event-ID`](https://html.spec.whatwg.org/multipage/server-sent-events.html#the-last-event-id-header) + header to indicate the last event ID it received. + - The server **MAY** use this header to replay messages that would have been sent + after the last event ID, _on the stream that was disconnected_, and to resume the + stream from that point. + - The server **MUST NOT** replay messages that would have been delivered on a + different stream. + +In other words, these event IDs should be assigned by servers on a _per-stream_ basis, to +act as a cursor within that particular stream. + +### Session Management + +An MCP "session" consists of logically related interactions between a client and a +server, beginning with the [initialization phase]({{< ref "lifecycle" >}}). To support +servers which want to establish stateful sessions: + +1. A server using the Streamable HTTP transport **MAY** assign a session ID at + initialization time, by including it in an `Mcp-Session-Id` header on the HTTP + response containing the `InitializeResult`. + - The session ID **SHOULD** be globally unique and cryptographically secure (e.g., a + securely generated UUID, a JWT, or a cryptographic hash). + - The session ID **MUST** only contain visible ASCII characters (ranging from 0x21 to + 0x7E). +2. If an `Mcp-Session-Id` is returned by the server during initialization, clients using + the Streamable HTTP transport **MUST** include it in the `Mcp-Session-Id` header on + all of their subsequent HTTP requests. + - Servers that require a session ID **SHOULD** respond to requests without an + `Mcp-Session-Id` header (other than initialization) with HTTP 400 Bad Request. +3. The server **MAY** terminate the session at any time, after which it **MUST** respond + to requests containing that session ID with HTTP 404 Not Found. +4. When a client receives HTTP 404 in response to a request containing an + `Mcp-Session-Id`, it **MUST** start a new session by sending a new `InitializeRequest` + without a session ID attached. +5. Clients that no longer need a particular session (e.g., because the user is leaving + the client application) **SHOULD** send an HTTP DELETE to the MCP endpoint with the + `Mcp-Session-Id` header, to explicitly terminate the session. + - The server **MAY** respond to this request with HTTP 405 Method Not Allowed, + indicating that the server does not allow clients to terminate sessions. + +### Sequence Diagram ```mermaid sequenceDiagram participant Client participant Server - Client->>Server: Open SSE connection - Server->>Client: endpoint event - loop Message Exchange - Client->>Server: HTTP POST messages - Server->>Client: SSE message events + note over Client, Server: initialization + + Client->>+Server: POST InitializeRequest + Server->>-Client: InitializeResponse<br>Mcp-Session-Id: 1868a90c... + + Client->>+Server: POST InitializedNotification<br>Mcp-Session-Id: 1868a90c... + Server->>-Client: 202 Accepted + + note over Client, Server: client requests + Client->>+Server: POST ... request ...<br>Mcp-Session-Id: 1868a90c... + + alt single HTTP response + Server->>Client: ... response ... + else server opens SSE stream + loop while connection remains open + Server-)Client: ... SSE messages from server ... + end + Server-)Client: SSE event: ... response ... + end + deactivate Server + + note over Client, Server: client notifications/responses + Client->>+Server: POST ... notification/response ...<br>Mcp-Session-Id: 1868a90c... + Server->>-Client: 202 Accepted + + note over Client, Server: server requests + Client->>+Server: GET<br>Mcp-Session-Id: 1868a90c... + loop while connection remains open + Server-)Client: ... SSE messages from server ... end - Client->>Server: Close SSE connection + deactivate Server + ``` +### Backwards Compatibility + +Clients and servers can maintain backwards compatibility with the deprecated [HTTP+SSE +transport]({{< ref "/specification/2024-11-05/basic/transports#http-with-sse" >}}) (from +protocol version 2024-11-05) as follows: + +**Servers** wanting to support older clients should: + +- Continue to host both the SSE and POST endpoints of the old transport, alongside the + new "MCP endpoint" defined for the Streamable HTTP transport. + - It is also possible to combine the old POST endpoint and the new MCP endpoint, but + this may introduce unneeded complexity. + +**Clients** wanting to support older servers should: + +1. Accept an MCP server URL from the user, which may point to either a server using the + old transport or the new transport. +2. Attempt to POST an `InitializeRequest` to the server URL, with an `Accept` header as + defined above: + - If it succeeds, the client can assume this is a server supporting the new Streamable + HTTP transport. + - If it fails with an HTTP 4xx status code (e.g., 405 Method Not Allowed or 404 Not + Found): + - Issue a GET request to the server URL, expecting that this will open an SSE stream + and return an `endpoint` event as the first event. + - When the `endpoint` event arrives, the client can assume this is a server running + the old HTTP+SSE transport, and should use that transport for all subsequent + communication. + ## Custom Transports Clients and servers **MAY** implement additional custom transport mechanisms to suit diff --git a/schema/draft/schema.ts b/schema/draft/schema.ts index 1d8df845..1d936f92 100644 --- a/schema/draft/schema.ts +++ b/schema/draft/schema.ts @@ -167,6 +167,7 @@ export interface InitializeResult extends Result { protocolVersion: string; capabilities: ServerCapabilities; serverInfo: Implementation; + /** * Instructions describing how to use the server and its features. *