Skip to content

Commit e148315

Browse files
authored
Merge pull request modelcontextprotocol#2260 from evalstate/sep/unsolicitited-req
SEP-2260 Require Server requests to be associated with a Client request.
2 parents afbb1da + 1ebb7a0 commit e148315

13 files changed

Lines changed: 687 additions & 32 deletions

docs/docs.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -403,6 +403,12 @@
403403
"seps/2085-governance-succession-and-amendment",
404404
"seps/2133-extensions"
405405
]
406+
},
407+
{
408+
"group": "Accepted",
409+
"pages": [
410+
"seps/2260-Require-Server-requests-to-be-associated-with-Client-requests"
411+
]
406412
}
407413
]
408414
},

docs/seps/1036-url-mode-elicitation-for-secure-out-of-band-intera.mdx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,12 +300,14 @@ Servers must verify that the user completing a URL elicitation is the same user
300300
### Implementation Requirements
301301

302302
1. **Clients must**:
303+
303304
- Use secure browser contexts that prevent inspection of user inputs
304305
- Validate URLs for SSRF protection
305306
- Obtain explicit user consent before opening URLs
306307
- Clearly display target domains
307308

308309
2. **Servers must**:
310+
309311
- Bind elicitation state to authenticated user sessions
310312
- Verify user identity at the beginning and end of a URL elicitation flow
311313
- Implement appropriate rate limiting

docs/seps/1577--sampling-with-tools.mdx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,7 @@ In the "Possible Follow ups" Section below, we give examples of features that we
177177
```
178178

179179
- Notes:
180+
180181
- Differences of role vs. content type when it comes to tool calling between APIs:
181182
- OpenAI: `role: “system" | “user" | “assistant" | “tool"` (where tool is for tool results), while tool calls are nested in assistant messages, content is then typically null but some “OpenAI compatible” APIs accept non-null values
182183
- ```typescript
@@ -311,6 +312,7 @@ Two bits needed here:
311312
- implicitly supports updating the cache (maybe even as subtree)
312313
- Cons: possibly harder to implement / more storage inefficient
313314
- Introduce allowed_tools feature to enable / disable tools w/o breaking context caching
315+
314316
- Relevant to this SEP as we may want to merge this feature [under the tool_choice field, similar to what OpenAI did](https://platform.openai.com/docs/guides/function-calling).
315317
316318
```typescript
Lines changed: 299 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,299 @@
1+
---
2+
title: "SEP-2260: Require Server requests to be associated with a Client request."
3+
sidebarTitle: "SEP-2260: Require Server requests to be associate…"
4+
description: "Require Server requests to be associated with a Client request."
5+
---
6+
7+
<div className="flex items-center gap-2 mb-4">
8+
<Badge color="blue" shape="pill">
9+
Accepted
10+
</Badge>
11+
<Badge color="gray" shape="pill">
12+
Standards Track
13+
</Badge>
14+
</div>
15+
16+
| Field | Value |
17+
| ------------- | ------------------------------------------------------------------------------- |
18+
| **SEP** | 2260 |
19+
| **Title** | Require Server requests to be associated with a Client request. |
20+
| **Status** | Accepted |
21+
| **Type** | Standards Track |
22+
| **Created** | 2026-02-16 |
23+
| **Author(s)** | MCP Transports Working Group |
24+
| **Sponsor** | [@CaitieM20](https://github.com/CaitieM20) - Caitie McCaffrey |
25+
| **PR** | [#2260](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2260) |
26+
27+
---
28+
29+
## Abstract
30+
31+
This SEP clarifies that `roots/list`, `sampling/createMessage`, and
32+
`elicitation/create` requests **MUST** be associated with an originating
33+
client-to-server request (e.g., during `tools/call`, `resources/read`, or
34+
`prompts/get` processing). Standalone server-initiated requests of these types
35+
outside notifications **MUST NOT** be implemented.
36+
37+
Although not enforced in the current MCP Data Layer, logically these requests
38+
**MUST** be associated with a valid client-to-server JSON-RPC Request Id.
39+
40+
The operational server-to-client **Ping** is excepted from this restriction.
41+
42+
## Motivation
43+
44+
### Current Specification
45+
46+
The current specification uses **SHOULD** language in the transport layer:
47+
48+
In context of responding to a POST Request in the Streamable HTTP transport [(2025-11-25/basic/transports.mdx:121-L123)](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/2025-11-25/docs/specification/2025-11-25/basic/transports.mdx?plain=1#L121-L123):
49+
50+
> - "The server **MAY** send JSON-RPC _requests_ and _notifications_ before sending the JSON-RPC _response_. These messages **SHOULD** relate to the originating client _request_."
51+
52+
For the optional GET SSE Stream [(2025-11-25/basic/transports.mdx:146-L148)](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/2025-11-25/docs/specification/2025-11-25/basic/transports.mdx?plain=1#L146C1-L148C32):
53+
54+
> - "The server **MAY** send JSON-RPC _requests_ and _notifications_ on the stream."
55+
> - "These messages **SHOULD** be unrelated to any concurrently-running JSON-RPC _request_ from the client."
56+
57+
Although the GET stream allows "unsolicited" requests, its use is entirely optional and cannot be relied upon by MCP Server authors.
58+
59+
### Design Intent
60+
61+
The design intent of MCP Server Requests is to operate reactively **nested within** other MCP operations:
62+
63+
- **Sampling** enables servers to request LLM assistance while processing a tool call, resource request, or prompt
64+
- **Elicitation** enables servers to gather additional user input needed to complete an operation
65+
- **List Roots** enables servers to identify shared storage locations
66+
67+
**Ping** has a special status as it is primarily intended as a keep-alive/health-check mechanism.
68+
69+
For Streamable HTTP Servers this enables SSE Streams to be maintained for extended periods if no Notifications or Requests are available to be sent. For client-to-server Requests they are associable. Future transport implementations will remove the need for dissociated Pings.
70+
71+
The current specification already describes this pattern:
72+
73+
> "Sampling in MCP allows servers to implement agentic behaviors, by enabling LLM calls to occur _nested_ inside other MCP server features."
74+
75+
However, the normative requirements don't enforce this constraint.
76+
77+
### Simplification Benefits
78+
79+
Making this constraint explicit:
80+
81+
1. **Simplifies transport implementations** - Transports don't need to support arbitrary server-initiated request/response flows, which require a persistent connection from Server to Client; they only need request-scoped bidirectional communication
82+
2. **Clarifies user experience** - Users understand that sampling/elicitation happens _because_ they initiated an action, not spontaneously
83+
3. **Reduces security surface** - Ensures client has context for what scope the additional requested information will be used for. This allows clients to make better informed decisions on whether to provide the requested info.
84+
4. **Aligns with practice** - Based on a scan of GitHub all existing implementations already follow this pattern, except one repo owned by the SEP author with a contrived scenario.
85+
86+
## Specification Changes
87+
88+
### 1. Add Warning Blocks to Feature Documentation
89+
90+
**In `client/sampling.mdx` (after existing security warning):**
91+
92+
```markdown
93+
<Warning>
94+
95+
**Request Association Requirement**
96+
97+
Servers **MUST** send `sampling/createMessage` requests only in association with an originating client request (e.g., during `tools/call`, `resources/read`, or `prompts/get` processing).
98+
99+
Standalone server-initiated sampling on independent communication streams (unrelated to any client request) is not supported and **MUST NOT** be implemented. Future transport implementations are not required to support this pattern.
100+
101+
</Warning>
102+
```
103+
104+
**In `client/elicitation.mdx` (after existing security warning):**
105+
106+
```markdown
107+
<Warning>
108+
109+
**Request Association Requirement**
110+
111+
Servers **MUST** send server-to-client requests (such as `roots/list`,
112+
`sampling/createMessage`, or `elicitation/create`) only in association with an
113+
originating client request (e.g., during `tools/call`, `resources/read`, or
114+
`prompts/get` processing).
115+
116+
Standalone server-initiated requests of these types on independent
117+
communication streams (unrelated to any client request) are not supported and
118+
**MUST NOT** be implemented. Future transport implementations are not required
119+
to support this pattern.
120+
121+
</Warning>
122+
```
123+
124+
**In `client/roots.mdx` (in `User Interaction Model` section):**
125+
126+
```markdown
127+
<Warning>
128+
129+
Servers **MUST** send server-to-client requests (such as `roots/list`,
130+
`sampling/createMessage`, or `elicitation/create`) only in association with an
131+
originating client request (e.g., during `tools/call`, `resources/read`, or
132+
`prompts/get` processing).
133+
134+
Standalone server-initiated requests of these types on independent
135+
communication streams (unrelated to any client request) are not supported and
136+
**MUST NOT** be implemented. Future transport implementations are not required
137+
to support this pattern.
138+
139+
</Warning>
140+
```
141+
142+
**In `basic/utilities/ping.mdx` (In `Overview` section):**
143+
144+
```markdown
145+
<Warning>
146+
147+
`ping` is an MCP-level liveness check and **MAY** be sent by either party at
148+
any time on an established session/connection.
149+
150+
In Streamable HTTP, implementations **SHOULD** prefer transport-level SSE
151+
keepalive mechanisms for idle-connection maintenance; `ping` remains available
152+
for protocol-level responsiveness checks.
153+
154+
Request-association requirements for `roots/list`, `sampling/createMessage`,
155+
and `elicitation/create` do not apply to `ping`.
156+
157+
</Warning>
158+
```
159+
160+
### 2. Clarify Transport Layer Constraints
161+
162+
**In `basic/transports.mdx`, POST-initiated SSE streams (line ~121):**
163+
164+
```diff
165+
- The server **MAY** send JSON-RPC _requests_ and _notifications_ before sending the
166+
- JSON-RPC _response_. These messages **SHOULD** relate to the originating client
167+
- _request_.
168+
+ The server **MAY** send JSON-RPC _requests_ and _notifications_ before sending the
169+
+ JSON-RPC _response_. These messages **MUST** relate to the originating client
170+
+ _request_.
171+
```
172+
173+
**In `basic/transports.mdx`, GET-initiated standalone SSE streams (line ~147):**
174+
175+
```diff
176+
- The server **MAY** send JSON-RPC _requests_ and _notifications_ on the stream.
177+
- These messages **SHOULD** be unrelated to any concurrently-running JSON-RPC
178+
- _request_ from the client.
179+
+ The server **MAY** send JSON-RPC _notifications_ and _pings_ on the stream.
180+
+ These messages **SHOULD** be unrelated to any concurrently-running JSON-RPC
181+
+ _request_ from the client, **except** that `roots/list`,
182+
+ `sampling/createMessage`, and `elicitation/create` requests **MUST NOT** be
183+
+ sent on standalone streams.
184+
```
185+
186+
## Backward Compatibility
187+
188+
### Impact Assessment
189+
190+
This change is expected to have **minimal to no impact** on existing implementations:
191+
192+
1. **Common usage patterns are preserved** - Sampling/elicitation within tool execution, resource reading, and prompt handling remain fully supported
193+
2. **No known implementations affected** - Research conducted on GitHub has shown only one implementation of this pattern. This singular implementation is owned by the SEP author.
194+
195+
### What's Disallowed
196+
197+
The following pattern, which was never explicitly documented or recommended, is now explicitly prohibited:
198+
199+
```python
200+
# ❌ PROHIBITED: Standalone server push
201+
async def background_task():
202+
while True:
203+
await asyncio.sleep(60)
204+
# Try to initiate sampling without any client request context
205+
await session.create_message(...) # NOT ALLOWED
206+
```
207+
208+
### What Remains Supported
209+
210+
The canonical pattern remains fully supported:
211+
212+
```python
213+
# ✅ SUPPORTED: Sampling during tool execution
214+
@mcp.tool()
215+
async def analyze_data(data: str, ctx: Context) -> str:
216+
# Request LLM analysis while processing the tool call
217+
result = await ctx.session.create_message(
218+
messages=[SamplingMessage(role="user", content=...)]
219+
)
220+
return result.content.text
221+
```
222+
223+
## Implementation Guidance
224+
225+
### For Server Implementers
226+
227+
**No changes required** if your server:
228+
229+
- Only uses server-to-client requests within tool handlers
230+
- Only uses server-to-client requests within resource/prompt handlers
231+
- Uses server-to-client requests synchronously as part of processing a client request
232+
233+
**Changes required** if your server:
234+
235+
- Attempts to initiate server-to-client requests on standalone HTTP GET streams
236+
- Attempts to send server-to-client requests requests independent of client operations
237+
- Has background tasks that try to invoke server-to-client requests
238+
239+
Alternative designs will need to be implemented for the "Changes Required" case.
240+
241+
Implementors performing unsolicited server-to-client requests (typically URL Elicitation) immediately following initialization are encouraged to lazily perform these requests within the scope of a client-to-server request that requires that information from the client.
242+
243+
### Timeout Considerations
244+
245+
When an MCP Server initiates a "nested" request inside a client request, the duration of the parent request extends to include the user's response time.
246+
247+
Implementers **MUST** ensure that:
248+
249+
1. Transport timeouts (e.g. HTTP Request Timeout) are sufficient to accommodate "Human-in-the-loop" delays, which may be unbounded.
250+
2. Short timeouts enforced by infrastructure (e.g. Load Balancers) may result in
251+
connection termination before the user responds. For Streamable HTTP,
252+
transport-level SSE keepalive mechanisms **SHOULD** be used to keep
253+
connections alive and reset timers; `ping` requests **MAY** additionally be
254+
used for protocol-level responsiveness checks.
255+
256+
### For Client Implementers
257+
258+
**No changes required** - Clients should already handle sampling/elicitation requests in the context of their own outbound requests. Potential to simplify implementations if out-of-band is currently supported.
259+
260+
Clients recieving server-to-client requests with no associated outbound request **SHOULD** respond with a `-32602` (Invalid Params) error.
261+
262+
### For Transport Implementers
263+
264+
Future transport implementations can rely on the guarantee that:
265+
266+
- Sampling/elicitation requests only occur within the scope of a client-initiated request
267+
- Transports don't need to support arbitrary server-initiated request/response flows on standalone channels
268+
- Request correlation and lifecycle management is simplified
269+
270+
## Timeline
271+
272+
(This SEP intends to serve as a public notice of the change prior to future protocol versions that will not be compatible with this usage)
273+
274+
## Alternatives Considered
275+
276+
### 1. Soft Deprecation
277+
278+
Use **SHOULD NOT** language to discourage but not prohibit the pattern.
279+
280+
**Rejected because:** The behavior was never intentionally supported, and leaving it ambiguous prevents transport simplification.
281+
282+
### 2. Keep Current Ambiguity
283+
284+
Leave the existing **SHOULD** language unchanged.
285+
286+
**Rejected because:** This blocks future transport implementations and leaves implementers uncertain about whether the pattern is supported.
287+
288+
### 3. Create a Capability Flag
289+
290+
Add a `sampling.standalone` or similar capability for servers that want this behavior.
291+
292+
**Rejected because:** This adds complexity for a use case with no known demand, and contradicts the "nested" design principle.
293+
294+
## References
295+
296+
- Current sampling documentation: `/specification/draft/client/sampling.mdx`
297+
- Current elicitation documentation: `/specification/draft/client/elicitation.mdx`
298+
- Transport specification: `/specification/draft/basic/transports.mdx`
299+
- User interaction model discussion in client concepts documentation

docs/seps/990-enable-enterprise-idp-policy-controls-during-mcp-o.mdx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,5 +84,4 @@ sequenceDiagram
8484
end
8585
```
8686

87-
> [!IMPORTANT]
88-
> **State:** Ready to Review
87+
> [!IMPORTANT] > **State:** Ready to Review

docs/seps/991-enable-url-based-client-registration-using-oauth-c.mdx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,11 +61,13 @@ Relatedly, there are many more MCP servers than there are clients (similar to ho
6161
Client ID Metadata Documents enable a unique trust model where:
6262

6363
1. **Servers can trust clients they've never seen before** based on:
64+
6465
- The HTTPS domain hosting the metadata
6566
- The metadata content itself
6667
- Domain reputation and security policies
6768

6869
2. **Servers maintain full control** through flexible policies:
70+
6971
- **Open Servers**: Can accept any HTTPS client_id, enabling maximum interoperability
7072
- **Protected Servers**: Can restrict to trusted domains or specific clients
7173

0 commit comments

Comments
 (0)