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>
This commit is contained in:
parent
3f39ba7936
commit
83098e7cc0
2 changed files with 963 additions and 20 deletions
|
|
@ -16,9 +16,16 @@ The C# SDK registers an instance of <xref:ModelContextProtocol.Server.McpServer>
|
|||
so tools can simply add a parameter of type <xref:ModelContextProtocol.Server.McpServer> to their method signature to access it.
|
||||
|
||||
The MCP Server must specify the schema of each input value it is requesting from the user.
|
||||
Only primitive types (string, number, boolean) are supported for elicitation requests.
|
||||
Primitive types (string, number, boolean) and enum types are supported for elicitation requests.
|
||||
The schema may include a description to help the user understand what is being requested.
|
||||
|
||||
For enum types, the SDK supports several schema formats:
|
||||
- **UntitledSingleSelectEnumSchema**: A single-select enum where the enum values serve as both the value and display text
|
||||
- **TitledSingleSelectEnumSchema**: A single-select enum with separate display titles for each option (using JSON Schema `oneOf` with `const` and `title`)
|
||||
- **UntitledMultiSelectEnumSchema**: A multi-select enum allowing multiple values to be selected
|
||||
- **TitledMultiSelectEnumSchema**: A multi-select enum with display titles for each option
|
||||
- **LegacyTitledEnumSchema** (deprecated): The legacy enum schema using `enumNames` for backward compatibility
|
||||
|
||||
The server can request a single input or multiple inputs at once.
|
||||
To help distinguish multiple inputs, each input has a unique name.
|
||||
|
||||
|
|
|
|||
|
|
@ -123,4 +123,152 @@ public sealed class InteractiveTools
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// <snippet_EnumExamples>
|
||||
[McpServerTool, Description("Example tool demonstrating various enum schema types")]
|
||||
public async Task<string> EnumExamples(
|
||||
McpServer server,
|
||||
CancellationToken token
|
||||
)
|
||||
{
|
||||
// Example 1: UntitledSingleSelectEnumSchema - Simple enum without display titles
|
||||
var prioritySchema = new RequestSchema
|
||||
{
|
||||
Properties =
|
||||
{
|
||||
["Priority"] = new UntitledSingleSelectEnumSchema
|
||||
{
|
||||
Title = "Priority Level",
|
||||
Description = "Select the priority level",
|
||||
Enum = ["low", "medium", "high", "critical"],
|
||||
Default = "medium"
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var priorityResponse = await server.ElicitAsync(new ElicitRequestParams
|
||||
{
|
||||
Message = "Select a priority level:",
|
||||
RequestedSchema = prioritySchema
|
||||
}, token);
|
||||
|
||||
if (priorityResponse.Action != "accept")
|
||||
{
|
||||
return "Operation cancelled";
|
||||
}
|
||||
|
||||
string? priority = priorityResponse.Content?["Priority"].GetString();
|
||||
|
||||
// Example 2: TitledSingleSelectEnumSchema - Enum with custom display titles
|
||||
var severitySchema = new RequestSchema
|
||||
{
|
||||
Properties =
|
||||
{
|
||||
["Severity"] = new TitledSingleSelectEnumSchema
|
||||
{
|
||||
Title = "Issue Severity",
|
||||
Description = "Select the issue severity level",
|
||||
OneOf =
|
||||
[
|
||||
new EnumSchemaOption { Const = "p0", Title = "P0 - Critical (Immediate attention required)" },
|
||||
new EnumSchemaOption { Const = "p1", Title = "P1 - High (Urgent, within 24 hours)" },
|
||||
new EnumSchemaOption { Const = "p2", Title = "P2 - Medium (Within a week)" },
|
||||
new EnumSchemaOption { Const = "p3", Title = "P3 - Low (As time permits)" }
|
||||
],
|
||||
Default = "p2"
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var severityResponse = await server.ElicitAsync(new ElicitRequestParams
|
||||
{
|
||||
Message = "Select the issue severity:",
|
||||
RequestedSchema = severitySchema
|
||||
}, token);
|
||||
|
||||
if (severityResponse.Action != "accept")
|
||||
{
|
||||
return "Operation cancelled";
|
||||
}
|
||||
|
||||
string? severity = severityResponse.Content?["Severity"].GetString();
|
||||
|
||||
// Example 3: UntitledMultiSelectEnumSchema - Select multiple values
|
||||
var tagsSchema = new RequestSchema
|
||||
{
|
||||
Properties =
|
||||
{
|
||||
["Tags"] = new UntitledMultiSelectEnumSchema
|
||||
{
|
||||
Title = "Tags",
|
||||
Description = "Select one or more tags",
|
||||
MinItems = 1,
|
||||
MaxItems = 3,
|
||||
Items = new UntitledEnumItemsSchema
|
||||
{
|
||||
Type = "string",
|
||||
Enum = ["bug", "feature", "documentation", "enhancement", "question"]
|
||||
},
|
||||
Default = ["bug"]
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var tagsResponse = await server.ElicitAsync(new ElicitRequestParams
|
||||
{
|
||||
Message = "Select up to 3 tags:",
|
||||
RequestedSchema = tagsSchema
|
||||
}, token);
|
||||
|
||||
if (tagsResponse.Action != "accept")
|
||||
{
|
||||
return "Operation cancelled";
|
||||
}
|
||||
|
||||
// For multi-select, the value is an array
|
||||
var tags = tagsResponse.Content?["Tags"].EnumerateArray()
|
||||
.Select(e => e.GetString())
|
||||
.ToArray();
|
||||
|
||||
// Example 4: TitledMultiSelectEnumSchema - Multi-select with custom titles
|
||||
var featuresSchema = new RequestSchema
|
||||
{
|
||||
Properties =
|
||||
{
|
||||
["Features"] = new TitledMultiSelectEnumSchema
|
||||
{
|
||||
Title = "Features",
|
||||
Description = "Select desired features",
|
||||
Items = new TitledEnumItemsSchema
|
||||
{
|
||||
AnyOf =
|
||||
[
|
||||
new EnumSchemaOption { Const = "auth", Title = "Authentication & Authorization" },
|
||||
new EnumSchemaOption { Const = "api", Title = "RESTful API" },
|
||||
new EnumSchemaOption { Const = "ui", Title = "Modern UI Components" },
|
||||
new EnumSchemaOption { Const = "db", Title = "Database Integration" }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var featuresResponse = await server.ElicitAsync(new ElicitRequestParams
|
||||
{
|
||||
Message = "Select desired features:",
|
||||
RequestedSchema = featuresSchema
|
||||
}, token);
|
||||
|
||||
if (featuresResponse.Action != "accept")
|
||||
{
|
||||
return "Operation cancelled";
|
||||
}
|
||||
|
||||
var features = featuresResponse.Content?["Features"].EnumerateArray()
|
||||
.Select(e => e.GetString())
|
||||
.ToArray();
|
||||
|
||||
return $"Selected: Priority={priority}, Severity={severity}, Tags=[{string.Join(", ", tags ?? [])}], Features=[{string.Join(", ", features ?? [])}]";
|
||||
}
|
||||
// </snippet_EnumExamples>
|
||||
}
|
||||
Loading…
Reference in a new issue