Skip to content

Commit 83098e7

Browse files
Copilotstephentoub
andauthored
Implement SEP-1330: Standards-compliant enum schemas for elicitation (#969)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com> Co-authored-by: Stephen Toub <stoub@microsoft.com>
1 parent 3f39ba7 commit 83098e7

File tree

9 files changed

+963
-20
lines changed

9 files changed

+963
-20
lines changed

docs/concepts/elicitation/elicitation.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,16 @@ The C# SDK registers an instance of <xref:ModelContextProtocol.Server.McpServer>
1616
so tools can simply add a parameter of type <xref:ModelContextProtocol.Server.McpServer> to their method signature to access it.
1717

1818
The MCP Server must specify the schema of each input value it is requesting from the user.
19-
Only primitive types (string, number, boolean) are supported for elicitation requests.
19+
Primitive types (string, number, boolean) and enum types are supported for elicitation requests.
2020
The schema may include a description to help the user understand what is being requested.
2121

22+
For enum types, the SDK supports several schema formats:
23+
- **UntitledSingleSelectEnumSchema**: A single-select enum where the enum values serve as both the value and display text
24+
- **TitledSingleSelectEnumSchema**: A single-select enum with separate display titles for each option (using JSON Schema `oneOf` with `const` and `title`)
25+
- **UntitledMultiSelectEnumSchema**: A multi-select enum allowing multiple values to be selected
26+
- **TitledMultiSelectEnumSchema**: A multi-select enum with display titles for each option
27+
- **LegacyTitledEnumSchema** (deprecated): The legacy enum schema using `enumNames` for backward compatibility
28+
2229
The server can request a single input or multiple inputs at once.
2330
To help distinguish multiple inputs, each input has a unique name.
2431

docs/concepts/elicitation/samples/server/Tools/InteractiveTools.cs

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,4 +123,152 @@ CancellationToken token
123123
}
124124
}
125125
}
126+
127+
// <snippet_EnumExamples>
128+
[McpServerTool, Description("Example tool demonstrating various enum schema types")]
129+
public async Task<string> EnumExamples(
130+
McpServer server,
131+
CancellationToken token
132+
)
133+
{
134+
// Example 1: UntitledSingleSelectEnumSchema - Simple enum without display titles
135+
var prioritySchema = new RequestSchema
136+
{
137+
Properties =
138+
{
139+
["Priority"] = new UntitledSingleSelectEnumSchema
140+
{
141+
Title = "Priority Level",
142+
Description = "Select the priority level",
143+
Enum = ["low", "medium", "high", "critical"],
144+
Default = "medium"
145+
}
146+
}
147+
};
148+
149+
var priorityResponse = await server.ElicitAsync(new ElicitRequestParams
150+
{
151+
Message = "Select a priority level:",
152+
RequestedSchema = prioritySchema
153+
}, token);
154+
155+
if (priorityResponse.Action != "accept")
156+
{
157+
return "Operation cancelled";
158+
}
159+
160+
string? priority = priorityResponse.Content?["Priority"].GetString();
161+
162+
// Example 2: TitledSingleSelectEnumSchema - Enum with custom display titles
163+
var severitySchema = new RequestSchema
164+
{
165+
Properties =
166+
{
167+
["Severity"] = new TitledSingleSelectEnumSchema
168+
{
169+
Title = "Issue Severity",
170+
Description = "Select the issue severity level",
171+
OneOf =
172+
[
173+
new EnumSchemaOption { Const = "p0", Title = "P0 - Critical (Immediate attention required)" },
174+
new EnumSchemaOption { Const = "p1", Title = "P1 - High (Urgent, within 24 hours)" },
175+
new EnumSchemaOption { Const = "p2", Title = "P2 - Medium (Within a week)" },
176+
new EnumSchemaOption { Const = "p3", Title = "P3 - Low (As time permits)" }
177+
],
178+
Default = "p2"
179+
}
180+
}
181+
};
182+
183+
var severityResponse = await server.ElicitAsync(new ElicitRequestParams
184+
{
185+
Message = "Select the issue severity:",
186+
RequestedSchema = severitySchema
187+
}, token);
188+
189+
if (severityResponse.Action != "accept")
190+
{
191+
return "Operation cancelled";
192+
}
193+
194+
string? severity = severityResponse.Content?["Severity"].GetString();
195+
196+
// Example 3: UntitledMultiSelectEnumSchema - Select multiple values
197+
var tagsSchema = new RequestSchema
198+
{
199+
Properties =
200+
{
201+
["Tags"] = new UntitledMultiSelectEnumSchema
202+
{
203+
Title = "Tags",
204+
Description = "Select one or more tags",
205+
MinItems = 1,
206+
MaxItems = 3,
207+
Items = new UntitledEnumItemsSchema
208+
{
209+
Type = "string",
210+
Enum = ["bug", "feature", "documentation", "enhancement", "question"]
211+
},
212+
Default = ["bug"]
213+
}
214+
}
215+
};
216+
217+
var tagsResponse = await server.ElicitAsync(new ElicitRequestParams
218+
{
219+
Message = "Select up to 3 tags:",
220+
RequestedSchema = tagsSchema
221+
}, token);
222+
223+
if (tagsResponse.Action != "accept")
224+
{
225+
return "Operation cancelled";
226+
}
227+
228+
// For multi-select, the value is an array
229+
var tags = tagsResponse.Content?["Tags"].EnumerateArray()
230+
.Select(e => e.GetString())
231+
.ToArray();
232+
233+
// Example 4: TitledMultiSelectEnumSchema - Multi-select with custom titles
234+
var featuresSchema = new RequestSchema
235+
{
236+
Properties =
237+
{
238+
["Features"] = new TitledMultiSelectEnumSchema
239+
{
240+
Title = "Features",
241+
Description = "Select desired features",
242+
Items = new TitledEnumItemsSchema
243+
{
244+
AnyOf =
245+
[
246+
new EnumSchemaOption { Const = "auth", Title = "Authentication & Authorization" },
247+
new EnumSchemaOption { Const = "api", Title = "RESTful API" },
248+
new EnumSchemaOption { Const = "ui", Title = "Modern UI Components" },
249+
new EnumSchemaOption { Const = "db", Title = "Database Integration" }
250+
]
251+
}
252+
}
253+
}
254+
};
255+
256+
var featuresResponse = await server.ElicitAsync(new ElicitRequestParams
257+
{
258+
Message = "Select desired features:",
259+
RequestedSchema = featuresSchema
260+
}, token);
261+
262+
if (featuresResponse.Action != "accept")
263+
{
264+
return "Operation cancelled";
265+
}
266+
267+
var features = featuresResponse.Content?["Features"].EnumerateArray()
268+
.Select(e => e.GetString())
269+
.ToArray();
270+
271+
return $"Selected: Priority={priority}, Severity={severity}, Tags=[{string.Join(", ", tags ?? [])}], Features=[{string.Join(", ", features ?? [])}]";
272+
}
273+
// </snippet_EnumExamples>
126274
}

0 commit comments

Comments
 (0)