diff --git a/docs/specification/draft/basic/transports/streamable-http.mdx b/docs/specification/draft/basic/transports/streamable-http.mdx
index 5c382a276..cfd0058bd 100644
--- a/docs/specification/draft/basic/transports/streamable-http.mdx
+++ b/docs/specification/draft/basic/transports/streamable-http.mdx
@@ -90,6 +90,20 @@ request to the MCP endpoint.
`Content-Type: text/event-stream` (an SSE response stream). The client
**MUST** support both.
+
+
+This revision of the core protocol defines no client-to-server
+_notifications_ over Streamable HTTP. The only client-sent notification in
+the core protocol, `notifications/cancelled`, is used only on the
+[stdio](/specification/draft/basic/transports/stdio) transport; on
+Streamable HTTP, closing the SSE response stream is itself the cancellation
+signal and no `notifications/cancelled` message is expected (see
+[Cancellation][cancellation]). The notification rules above describe the
+transport mechanics for a notification POST; header requirements for
+notification POSTs are not defined by this revision.
+
+
+
## Receiving Messages
When the server returns an SSE response stream
@@ -260,11 +274,15 @@ the header per [Server Validation](#server-validation).
| Header Name | Source Field | Required For |
| ------------ | ----------------------------- | ------------------------------------------------------ |
-| `Mcp-Method` | `method` | All requests and notifications |
+| `Mcp-Method` | `method` | All requests |
| `Mcp-Name` | `params.name` or `params.uri` | `tools/call`, `resources/read`, `prompts/get` requests |
These headers are **REQUIRED** for compliance.
+If the `Mcp-Name` source value cannot be safely represented as a plain ASCII
+header value, clients **MUST** encode it using the Base64 sentinel format
+described in [Value Encoding](#value-encoding).
+
**`tools/call` request:**
```http
@@ -354,8 +372,19 @@ the header name `Mcp-Param-{name}`.
string, boolean). Parameters with type `number` are not permitted.
Integer values **MUST** be within the safe range for JavaScript
(−253+1 to 253−1)
-- **MAY** be applied to properties at any nesting depth within the
- `inputSchema`, not only top-level properties
+- **MUST** only be applied to properties that are _statically reachable_
+ from the schema root: reachable via a chain consisting solely of
+ `properties` keys. The chain **MUST NOT** pass through `items` (or any
+ other array keyword), composition keywords (`oneOf`, `anyOf`, `allOf`,
+ `not`), conditional keywords (`if`/`then`/`else`), or `$ref`. Nested
+ object properties are permitted as long as every step in the chain is a
+ `properties` key. An `x-mcp-header` annotation anywhere else makes the
+ annotation — and thus the tool definition — invalid.
+
+Header extraction is defined as reading the instance value at the exact
+property path of the annotated property (the chain of `properties` keys
+leading to it). If no value is present at that path in the call arguments,
+the header is omitted.
Clients using the Streamable HTTP transport **MUST** reject tool definitions
where any `x-mcp-header` value violates these constraints. Rejection means
@@ -445,10 +474,21 @@ representation with the following format:
Mcp-Param-{Name}: =?base64?{Base64EncodedValue}?=
```
+The same encoding rule applies to the `Mcp-Name` header value. Tool and
+prompt names are only **SHOULD**-constrained to header-safe characters, so a
+name (or resource URI) outside the safe set is carried as:
+
+```text
+Mcp-Name: =?base64?{Base64EncodedValue}?=
+```
+
The prefix `=?base64?` and suffix `?=` indicate that the value is
Base64-encoded. These markers are case-sensitive and **MUST** appear exactly
as shown (lowercase). Servers and intermediaries that need to inspect these
-values **MUST** decode them accordingly.
+values **MUST** decode them accordingly. In particular, servers **MUST**
+decode an encoded `Mcp-Name` or `Mcp-Param-{Name}` value before comparing it
+to the corresponding request body value during
+[Server Validation](#server-validation).
To avoid ambiguity, clients **MUST** also Base64-encode any plain-ASCII
value that matches the sentinel pattern (i.e., starts with `=?base64?`
@@ -476,7 +516,9 @@ When constructing a `tools/call` request via HTTP transport, the client
2. Append the `Mcp-Method` header and, if applicable, `Mcp-Name` header to
the request.
3. Inspect the tool's `inputSchema` for properties marked with
- `x-mcp-header` and extract the value for each parameter.
+ `x-mcp-header` and extract the value at each annotated property's exact
+ property path, omitting the header when no value is present (see
+ [Schema Extension](#schema-extension)).
4. Encode the values according to the [Value Encoding](#value-encoding)
rules.
5. Append a `Mcp-Param-{Name}: {Value}` header to the request.
@@ -575,6 +617,9 @@ Validation failure conditions include:
- A required standard header (`MCP-Protocol-Version`, `Mcp-Method`,
`Mcp-Name`) is missing.
- A header value does not match the corresponding request body value.
+ For headers that permit the Base64 sentinel encoding (`Mcp-Name` and
+ `Mcp-Param-{Name}`), servers **MUST** decode encoded values (see
+ [Value Encoding](#value-encoding)) before comparing them to the body value.
- A header value contains invalid characters.
diff --git a/docs/specification/draft/changelog.mdx b/docs/specification/draft/changelog.mdx
index aa2445d3c..4008ff5c8 100644
--- a/docs/specification/draft/changelog.mdx
+++ b/docs/specification/draft/changelog.mdx
@@ -50,6 +50,14 @@ the previous revision, [2025-11-25](/specification/2025-11-25).
`structuredContent` to allow any JSON value. Add `$ref` resolution requirements and
composition-keyword resource bounds
([SEP-2106](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2106)).
+11. Remove the `notifications/elicitation/complete` notification and the
+ `elicitationId` field of URL mode elicitation requests, both introduced in
+ `2025-11-25`. Under the
+ [Multi Round-Trip Requests](/specification/draft/basic/patterns/mrtr) pattern, the
+ client learns the outcome of an out-of-band interaction by retrying the original
+ request, so a server-initiated completion signal — and the identifier used to
+ correlate it — no longer fit the protocol. Servers needing to correlate an
+ elicitation across retries encode their own identifier in `requestState`.
## Deprecated
diff --git a/docs/specification/draft/client/elicitation.mdx b/docs/specification/draft/client/elicitation.mdx
index 3164f456d..45f22463b 100644
--- a/docs/specification/draft/client/elicitation.mdx
+++ b/docs/specification/draft/client/elicitation.mdx
@@ -331,10 +331,9 @@ URL mode elicitation enables servers to direct users to external URLs for out-of
URL mode elicitation requests **MUST** specify `mode: "url"`, a `message`, and include these additional parameters:
-| Name | Type | Description |
-| --------------- | ------ | ----------------------------------------- |
-| `url` | string | The URL that the user should navigate to. |
-| `elicitationId` | string | A unique identifier for the elicitation. |
+| Name | Type | Description |
+| ----- | ------ | ----------------------------------------- |
+| `url` | string | The URL that the user should navigate to. |
The `url` parameter **MUST** contain a valid URL.
@@ -360,7 +359,6 @@ The same request could direct the user into an OAuth authorization flow, or a pa
"method": "elicitation/create",
"params": {
"mode": "url",
- "elicitationId": "550e8400-e29b-41d4-a716-446655440000",
"url": "https://mcp.example.com/ui/set_api_key",
"message": "Please provide your API key to continue."
}
@@ -377,35 +375,12 @@ The same request could direct the user into an OAuth authorization flow, or a pa
The response with `action: "accept"` indicates that the user has consented to the
interaction. It does not mean that the interaction is complete. The interaction occurs out
-of band and the client is not aware of the outcome until and unless the server sends a notification indicating completion.
-
-### Completion Notifications for URL Mode Elicitation
-
-Servers **MAY** send a `notifications/elicitation/complete` notification when an
-out-of-band interaction started by URL mode elicitation is completed. This allows clients to react programmatically if appropriate.
-
-Servers sending notifications:
-
-- **MUST** only send the notification to the client that initiated the elicitation request.
-- **MUST** include the `elicitationId` established in the original `elicitation/create` request.
-
-Clients:
-
-- **MUST** ignore notifications referencing unknown or already-completed IDs.
-- **MAY** wait for this notification to automatically retry requests that received an [`InputRequiredResult`](/specification/draft/basic/patterns/mrtr#inputrequiredresult) (echoing back its `requestState`), update the user interface, or otherwise continue an interaction.
-- **SHOULD** still provide manual controls that let the user retry or cancel the original request (or otherwise resume interacting with the client) if the notification never arrives.
-
-#### Example
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "notifications/elicitation/complete",
- "params": {
- "elicitationId": "550e8400-e29b-41d4-a716-446655440000"
- }
-}
-```
+of band and the client is not directly informed of the outcome. When the client retries
+the original request, the server determines from the echoed `requestState` (or its own
+stored state) whether the out-of-band interaction has completed, and either returns the
+final result or responds with another `InputRequiredResult`. Clients **SHOULD** provide
+manual controls that let the user retry or cancel the original request (or otherwise
+resume interacting with the client).
## Message Flow
@@ -451,7 +426,6 @@ sequenceDiagram
Note over User,UserAgent: User interaction
UserAgent-->>Server: Interaction complete
- Server-->>Client: notifications/elicitation/complete (optional)
Note over Server: Continue processing with new information
Server-->Client: Result(id: 2, result)
@@ -685,7 +659,7 @@ To prevent this attack, the server **MUST** ensure that the user who started the
There are many ways to achieve this and the best way will depend on the specific implementation.
As a common, non-normative example, consider a case where the MCP server is accessible via the web and desires to perform a third-party authorization code flow.
-To prevent the phishing attack, the server would create a URL mode elicitation to `https://mcp.example.com/connect?elicitationId=...` rather than the third-party authorization endpoint.
+To prevent the phishing attack, the server would create a URL mode elicitation to `https://mcp.example.com/connect?...` rather than the third-party authorization endpoint.
This "connect URL" must ensure the user who opened the page is the same user for whom the elicitation was generated.
It would, for example, check that the user has a valid session cookie and that the session cookie is for the same user who was using the MCP client to generate the URL mode elicitation.
This could be done by comparing the authoritative subject (`sub` claim) from the MCP server's authorization server to the subject from the session cookie.
diff --git a/docs/specification/draft/schema.mdx b/docs/specification/draft/schema.mdx
index 7012731b8..83b1e40e9 100644
--- a/docs/specification/draft/schema.mdx
+++ b/docs/specification/draft/schema.mdx
@@ -536,8 +536,7 @@ Only top-level properties are allowed, without nesting.
The parameters for a request to elicit information from the user via a URL in the client.
Example: Elicit sensitive data
{ "mode": "url", "elicitationId": "550e8400-e29b-41d4-a716-446655440000", "url": "https://mcp.example.com/ui/set_api_key", "message": "Please provide your API key to continue." }
mode: "url"
The elicitation mode.
message: string
The message to present to the user explaining why the interaction is needed.
elicitationId: string
The ID of the elicitation, which must be unique within the context of the server.
-The client MUST treat this ID as an opaque value.
The parameters for a request to elicit information from the user via a URL in the client.