Skip to content

Commit 3f658d2

Browse files
committed
Nest scope
1 parent 43d25f4 commit 3f658d2

5 files changed

Lines changed: 322 additions & 165 deletions

File tree

docs/protocol/draft/schema.mdx

Lines changed: 63 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1572,15 +1572,9 @@ See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/exte
15721572
<ResponseField name="message" type={"string"} required>
15731573
A human-readable message describing what input is needed.
15741574
</ResponseField>
1575-
<ResponseField name="requestId" type={<><span><a href="#requestid">RequestId</a></span><span> | null</span></>} >
1576-
Optional request ID if this elicitation is tied to a specific request.
1577-
</ResponseField>
1578-
<ResponseField name="sessionId" type={<><span><a href="#sessionid">SessionId</a></span><span> | null</span></>} >
1579-
Optional session ID if this elicitation is tied to a specific session.
1580-
</ResponseField>
1581-
<ResponseField name="toolCallId" type={<><span><a href="#toolcallid">ToolCallId</a></span><span> | null</span></>} >
1582-
Optional tool call ID if this elicitation is tied to a specific tool call.
1583-
When present, `sessionId` should also be set.
1575+
<ResponseField name="scope" type={<><span><a href="#elicitationscope">ElicitationScope</a></span><span> | null</span></>} >
1576+
Optional scope for this elicitation.
1577+
When absent, the elicitation is standalone and not tied to any specific context.
15841578
</ResponseField>
15851579

15861580
**Variants:**
@@ -3555,6 +3549,66 @@ Type discriminator for elicitation schemas.
35553549
Object schema type.
35563550
</ResponseField>
35573551

3552+
## <span class="font-mono">ElicitationScope</span>
3553+
3554+
**UNSTABLE**
3555+
3556+
This capability is not part of the spec yet, and may be removed or changed at any point.
3557+
3558+
The scope of an elicitation request, determining what context it's tied to.
3559+
3560+
**Type:** Union
3561+
3562+
<ResponseField name="Object" type="object">
3563+
Tied to a session, optionally to a specific tool call within that session.
3564+
3565+
When `tool_call_id` is set, the elicitation is tied to a specific tool call.
3566+
This is useful when an agent receives an elicitation from an MCP server
3567+
during a tool call and needs to redirect it to the user.
3568+
3569+
<Expandable title="Properties">
3570+
3571+
<ResponseField
3572+
name="sessionId"
3573+
type={<a href="#sessionid">SessionId</a>}
3574+
required
3575+
>
3576+
The session this elicitation is tied to.
3577+
</ResponseField>
3578+
<ResponseField
3579+
name="toolCallId"
3580+
type={
3581+
<>
3582+
<span>
3583+
<a href="#toolcallid">ToolCallId</a>
3584+
</span>
3585+
<span> | null</span>
3586+
</>
3587+
}
3588+
>
3589+
Optional tool call within the session.
3590+
</ResponseField>
3591+
3592+
</Expandable>
3593+
</ResponseField>
3594+
3595+
<ResponseField name="Object" type="object">
3596+
Tied to a specific JSON-RPC request outside of a session
3597+
(e.g., during auth/configuration phases before any session is started).
3598+
3599+
<Expandable title="Properties">
3600+
3601+
<ResponseField
3602+
name="requestId"
3603+
type={<a href="#requestid">RequestId</a>}
3604+
required
3605+
>
3606+
The request this elicitation is tied to.
3607+
</ResponseField>
3608+
3609+
</Expandable>
3610+
</ResponseField>
3611+
35583612
## <span class="font-mono">ElicitationStringType</span>
35593613

35603614
Items definition for untitled multi-select enum properties.

docs/rfds/elicitation.mdx

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -357,8 +357,10 @@ The agent sends an `elicitation/create` request when it needs information from t
357357
"id": 43,
358358
"method": "elicitation/create",
359359
"params": {
360-
"sessionId": "...",
361-
"toolCallId": "tc_123",
360+
"scope": {
361+
"sessionId": "...",
362+
"toolCallId": "tc_123"
363+
},
362364
"mode": "form",
363365
"message": "How would you like me to approach this refactoring?",
364366
"requestedSchema": {
@@ -397,13 +399,12 @@ The agent sends an `elicitation/create` request when it needs information from t
397399
}
398400
```
399401

400-
`sessionId`, `requestId`, and `toolCallId` are all optional. Elicitation supports three scoping variants:
402+
The optional `scope` field determines what context the elicitation is tied to. Elicitation supports two scoping variants:
401403

402-
- **Session elicitation**: `sessionId` is set — tied to a specific session.
403-
- **Tool call elicitation**: `sessionId` and `toolCallId` are both set — tied to a specific tool call within a session. This is useful when an agent receives an elicitation request from an MCP server during a tool call and needs to redirect it to the user.
404-
- **Request elicitation**: `requestId` is set — tied to a specific JSON-RPC request outside of a session (e.g., auth/configuration phases before any session is started).
404+
- **Session scope**: `scope.sessionId` is set — tied to a specific session. Optionally includes `scope.toolCallId` when tied to a specific tool call within that session (e.g., when an agent receives an elicitation from an MCP server during a tool call and needs to redirect it to the user).
405+
- **Request scope**: `scope.requestId` is set — tied to a specific JSON-RPC request outside of a session (e.g., auth/configuration phases before any session is started).
405406

406-
An elicitation may also be sent without any of these fields, in which case it is standalone and not tied to any specific scope.
407+
When `scope` is omitted entirely, the elicitation is standalone and not tied to any specific context.
407408

408409
**Request-scoped example:**
409410

@@ -413,7 +414,9 @@ An elicitation may also be sent without any of these fields, in which case it is
413414
"id": 45,
414415
"method": "elicitation/create",
415416
"params": {
416-
"requestId": 12,
417+
"scope": {
418+
"requestId": 12
419+
},
417420
"mode": "form",
418421
"message": "Please provide your workspace name to continue setup.",
419422
"requestedSchema": {

schema/schema.unstable.json

Lines changed: 51 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1694,38 +1694,16 @@
16941694
"description": "A human-readable message describing what input is needed.",
16951695
"type": "string"
16961696
},
1697-
"requestId": {
1698-
"anyOf": [
1699-
{
1700-
"$ref": "#/$defs/RequestId"
1701-
},
1702-
{
1703-
"type": "null"
1704-
}
1705-
],
1706-
"description": "Optional request ID if this elicitation is tied to a specific request."
1707-
},
1708-
"sessionId": {
1709-
"anyOf": [
1710-
{
1711-
"$ref": "#/$defs/SessionId"
1712-
},
1713-
{
1714-
"type": "null"
1715-
}
1716-
],
1717-
"description": "Optional session ID if this elicitation is tied to a specific session."
1718-
},
1719-
"toolCallId": {
1697+
"scope": {
17201698
"anyOf": [
17211699
{
1722-
"$ref": "#/$defs/ToolCallId"
1700+
"$ref": "#/$defs/ElicitationScope"
17231701
},
17241702
{
17251703
"type": "null"
17261704
}
17271705
],
1728-
"description": "Optional tool call ID if this elicitation is tied to a specific tool call.\nWhen present, `sessionId` should also be set."
1706+
"description": "Optional scope for this elicitation.\nWhen absent, the elicitation is standalone and not tied to any specific context."
17291707
}
17301708
},
17311709
"required": ["message"],
@@ -2307,6 +2285,54 @@
23072285
}
23082286
]
23092287
},
2288+
"ElicitationScope": {
2289+
"anyOf": [
2290+
{
2291+
"description": "Tied to a session, optionally to a specific tool call within that session.\n\nWhen `tool_call_id` is set, the elicitation is tied to a specific tool call.\nThis is useful when an agent receives an elicitation from an MCP server\nduring a tool call and needs to redirect it to the user.",
2292+
"properties": {
2293+
"sessionId": {
2294+
"allOf": [
2295+
{
2296+
"$ref": "#/$defs/SessionId"
2297+
}
2298+
],
2299+
"description": "The session this elicitation is tied to."
2300+
},
2301+
"toolCallId": {
2302+
"anyOf": [
2303+
{
2304+
"$ref": "#/$defs/ToolCallId"
2305+
},
2306+
{
2307+
"type": "null"
2308+
}
2309+
],
2310+
"description": "Optional tool call within the session."
2311+
}
2312+
},
2313+
"required": ["sessionId"],
2314+
"title": "Session",
2315+
"type": "object"
2316+
},
2317+
{
2318+
"description": "Tied to a specific JSON-RPC request outside of a session\n(e.g., during auth/configuration phases before any session is started).",
2319+
"properties": {
2320+
"requestId": {
2321+
"allOf": [
2322+
{
2323+
"$ref": "#/$defs/RequestId"
2324+
}
2325+
],
2326+
"description": "The request this elicitation is tied to."
2327+
}
2328+
},
2329+
"required": ["requestId"],
2330+
"title": "Request",
2331+
"type": "object"
2332+
}
2333+
],
2334+
"description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nThe scope of an elicitation request, determining what context it's tied to."
2335+
},
23102336
"ElicitationStringType": {
23112337
"description": "Items definition for untitled multi-select enum properties.",
23122338
"oneOf": [

src/bin/generate.rs

Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -339,15 +339,29 @@ starting with '$/' it is free to ignore the notification."
339339
writeln!(&mut self.output, "**Type:** Union").unwrap();
340340
writeln!(&mut self.output).unwrap();
341341

342-
if let Some(shared_props) = definition.get("properties").and_then(|v| v.as_object())
343-
&& !shared_props.is_empty()
344-
{
345-
writeln!(&mut self.output, "**Shared properties:**").unwrap();
346-
writeln!(&mut self.output).unwrap();
347-
self.document_properties_as_fields(shared_props, definition, 0);
348-
writeln!(&mut self.output).unwrap();
349-
writeln!(&mut self.output, "**Variants:**").unwrap();
350-
writeln!(&mut self.output).unwrap();
342+
let discriminator_prop = definition
343+
.get("discriminator")
344+
.and_then(|d| d.get("propertyName"))
345+
.and_then(|p| p.as_str());
346+
347+
// Union types with top-level "properties" alongside "oneOf"/"anyOf" use them
348+
// as shared properties that apply to all variants (e.g., _meta, message).
349+
// The discriminator property (if any) is excluded since it's per-variant.
350+
if let Some(shared_props) = definition.get("properties").and_then(|v| v.as_object()) {
351+
let filtered_props: serde_json::Map<String, Value> = shared_props
352+
.iter()
353+
.filter(|(key, _)| Some(key.as_str()) != discriminator_prop)
354+
.map(|(k, v)| (k.clone(), v.clone()))
355+
.collect();
356+
357+
if !filtered_props.is_empty() {
358+
writeln!(&mut self.output, "**Shared properties:**").unwrap();
359+
writeln!(&mut self.output).unwrap();
360+
self.document_properties_as_fields(&filtered_props, definition, 0);
361+
writeln!(&mut self.output).unwrap();
362+
writeln!(&mut self.output, "**Variants:**").unwrap();
363+
writeln!(&mut self.output).unwrap();
364+
}
351365
}
352366

353367
let variants = definition
@@ -1212,13 +1226,20 @@ starting with '$/' it is free to ignore the notification."
12121226
let mut generator = MarkdownGenerator::new();
12131227
let definition = json!({
12141228
"description": "Example union.",
1229+
"discriminator": {
1230+
"propertyName": "mode"
1231+
},
12151232
"properties": {
12161233
"message": {
12171234
"type": "string",
12181235
"description": "Shared message."
1236+
},
1237+
"mode": {
1238+
"type": "string",
1239+
"description": "The discriminator."
12191240
}
12201241
},
1221-
"required": ["message"],
1242+
"required": ["message", "mode"],
12221243
"oneOf": [
12231244
{
12241245
"description": "First variant.",
@@ -1263,7 +1284,12 @@ starting with '$/' it is free to ignore the notification."
12631284
assert!(
12641285
generator
12651286
.output
1266-
.contains("<ResponseField name=\"url\" type=\"object\">")
1287+
.contains("<ResponseField name=\"url\" type=\"object\">"),
1288+
);
1289+
let shared_section = generator.output.split("**Variants:**").next().unwrap_or("");
1290+
assert!(
1291+
!shared_section.contains("<ResponseField name=\"mode\""),
1292+
"discriminator property 'mode' should not appear in shared properties"
12671293
);
12681294
}
12691295
}

0 commit comments

Comments
 (0)