SEP-1330:信息征询枚举 Schema 改进与标准合规
社区译文;来源: MCP 1255d50e
| 字段 | 值 |
|---|---|
| SEP | 1330 |
| 标题 | 信息征询枚举 Schema 改进与标准合规 |
| 状态 | Final |
| 类型 | 标准跟踪 |
| 创建日期 | 2025-08-11 |
| 作者 | chughtapan |
| 发起人 | 无 |
| PR | #1330 |
摘要
本 SEP 建议改进 MCP 中的枚举 schema 定义:弃用非标准的 enumNames 属性,改用符合 JSON Schema 标准的模式;除单选 schema 外,还增加对多选枚举 schema 的支持。新 schema 已依据 JSON 规范完成验证。
Schema 变更: https://github.com/modelcontextprotocol/modelcontextprotocol/pull/1148 TypeScript SDK 变更:https://github.com/modelcontextprotocol/typescript-sdk/pull/1077 Python SDK 变更:https://github.com/modelcontextprotocol/python-sdk/pull/1246 客户端实现: https://github.com/evalstate/fast-agent/pull/324/files 可运行演示: https://asciinema.org/a/anBvJdqEmTjw0JkKYOooQa5Ta
动机
现有枚举 schema 使用非标准方式为枚举值添加标题。它还将信息征询中枚举的使用(以及未来任何应采用 EnumSchema 的其他 schema 对象)限制为单选模式。要求用户选择多个条目是一种常见模式。在用户界面中,这相当于复选框与单选按钮之间的区别。
基于这些原因,我们建议对 EnumSchema 进行以下非破坏性的小幅改进,以改善用户和开发者体验。
- 保留现有的
EnumSchema,并将其标记为“Legacy”- 它使用非标准方式为枚举值添加标题
- 将其标记为 Legacy,但目前仍予以支持
- 按照 @dsp-ant 的建议,在建立适当的弃用策略后,我们会将其标记为已弃用
- 区分无标题枚举和带标题枚举
- 如果枚举值本身已足够清晰,则不必为每个值另行指定标题
- 如果枚举值不适合直接显示,则可为每个值指定标题
- 区分单选枚举和多选枚举
- 如果只能选择一个值,可以使用单选 schema
- 如果可以选择多个值,可以使用多选 schema
- 在
ElicitResponse中将数组加入additionalProperty类型- 允许将多个选中的枚举值返回给服务器
规范
1. 将使用非标准 enumNames 属性的当前 EnumSchema 标记为“Legacy”
当前 MCP 规范使用非标准的 enumNames 属性为枚举值提供显示名称。我们建议将 enumNames 属性标记为 legacy,并建议使用下文定义的、符合标准的枚举类型 TitledSingleSelectEnum。
// Continue to support the current EnumSchema as Legacy
/**
* Legacy: Use TitledSingleSelectEnumSchema instead.
* This interface will be removed in a future version.
*/
export interface LegacyEnumSchema {
type: "string";
title?: string;
description?: string;
enum: string[];
enumNames?: string[]; // Titles for enum values (non-standard, legacy)
}2. 定义单选枚举(带标题和无标题两种)
枚举可能需要标题,也可能不需要。枚举值本身可能具备可读性并适合直接显示。在这种情况下,使用 JSON Schema 关键字 enum 的无标题实现更简单。若要添加标题,则需要用由 const 和 title 构成的对象数组替换 enum 数组。
// Single select enum without titles
export type UntitledSingleSelectEnumSchema = {
type: "string";
title?: string;
description?: string;
enum: string[]; // Plain enum without titles
};
// Single select enum with titles
export type TitledSingleSelectEnumSchema = {
type: "string";
title?: string;
description?: string;
oneOf: Array<{
const: string; // Enum value
title: string; // Display name for enum value
}>;
};
// Combined single selection enumeration
export type SingleSelectEnumSchema =
UntitledSingleSelectEnumSchema | TitledSingleSelectEnumSchema;3. 引入多选枚举(带标题和无标题两种)
信息征询不支持数组和对象等任意 JSON 类型,以便客户端能够轻松显示选择选项;但多选枚举可以轻松实现。
// Multiple select enums without titles
export type UntitledMultiSelectEnumSchema = {
type: "array";
title?: string;
description?: string;
minItems?: number; // Minimum number of items to choose
maxItems?: number; // Maximum number of items to choose
items: {
type: "string";
enum: string[]; // Plain enum without titles
};
};
// Multiple select enums with titles
export type TitledMultiSelectEnumSchema = {
type: "array";
title?: string;
description?: string;
minItems?: number; // Minimum number of items to choose
maxItems?: number; // Maximum number of items to choose
items: {
oneOf: Array<{
const: string; // Enum value
title: string; // Display name for enum value
}>;
};
};
// Combined Multiple select enumeration
export type MultiSelectEnumSchema =
UntitledMultiSelectEnumSchema | TitledMultiSelectEnumSchema;4. 将所有类型合并为 EnumSchema
最终的 EnumSchema 将 legacy、多选和单选 schema 汇总为一个类型,其定义如下:
// Combined legacy, multiple, and single select enumeration
export type EnumSchema =
SingleSelectEnumSchema | MultiSelectEnumSchema | LegacyEnumSchema;5. 扩展 ElicitResult
当前的信息征询结果 schema 只允许返回基本类型。我们扩展该 schema,使其能够为 MultiSelectEnums 返回字符串数组:
export interface ElicitResult extends Result {
action: "accept" | "decline" | "cancel";
content?: { [key: string]: string | number | boolean | string[] }; // string[] is new
}实例 Schema 示例
无标题单选(无变化)
{
"type": "string",
"title": "Color Selection",
"description": "Choose your favorite color",
"enum": ["Red", "Green", "Blue"],
"default": "Green"
}带标题的 Legacy 单选
{
"type": "string",
"title": "Color Selection",
"description": "Choose your favorite color",
"enum": ["#FF0000", "#00FF00", "#0000FF"],
"enumNames": ["Red", "Green", "Blue"],
"default": "#00FF00"
}带标题单选
{
"type": "string",
"title": "Color Selection",
"description": "Choose your favorite color",
"oneOf": [
{ "const": "#FF0000", "title": "Red" },
{ "const": "#00FF00", "title": "Green" },
{ "const": "#0000FF", "title": "Blue" }
],
"default": "#00FF00"
}无标题多选
{
"type": "array",
"title": "Color Selection",
"description": "Choose your favorite colors",
"minItems": 1,
"maxItems": 3,
"items": {
"type": "string",
"enum": ["Red", "Green", "Blue"]
},
"default": ["Green"]
}带标题多选
{
"type": "array",
"title": "Color Selection",
"description": "Choose your favorite colors",
"minItems": 1,
"maxItems": 3,
"items": {
"anyOf": [
{ "const": "#FF0000", "title": "Red" },
{ "const": "#00FF00", "title": "Green" },
{ "const": "#0000FF", "title": "Blue" }
]
},
"default": ["Green"]
}理由
- 标准合规:与正式 JSON Schema 规范保持一致。标准模式可与现有 JSON Schema 验证器配合使用
- 灵活性:单选和多选枚举都支持纯枚举值及带显示名称的枚举值
- 客户端实现: 表明实现一组复选框相较于实现单个复选框,额外开销很小:https://github.com/evalstate/fast-agent/pull/324/files
向后兼容性
LegacyEnumSchema 类型在迁移期间保持向后兼容。使用 enumNames 的现有实现将继续工作,直至实施协议范围的弃用策略并移除此 schema。
参考实现
Schema 变更: https://github.com/modelcontextprotocol/modelcontextprotocol/pull/1148 TypeScript SDK 变更:https://github.com/modelcontextprotocol/typescript-sdk/pull/1077 Python SDK 变更:https://github.com/modelcontextprotocol/python-sdk/pull/1246 客户端实现: https://github.com/evalstate/fast-agent/pull/324/files 可运行演示: https://asciinema.org/a/anBvJdqEmTjw0JkKYOooQa5Ta
安全考量
未发现安全影响。此变更只涉及 schema 结构和标准合规。
附录
验证
使用 https://www.jsonschemavalidator.net/ 中保存的验证,我们验证:
- 本文中的所有实例 schema 示例是否符合下一节建议的 JSON meta-schema
EnumSchema - 有效值和无效值是否符合本文中的实例 schema 示例
Legacy 单选
EnumSchema验证带标题的 legacy 单选实例 schema- 带标题的 legacy 单选实例 schema 验证正确的单选值
- 带标题的 legacy 单选实例 schema 验证错误的单选值
单选
EnumSchema验证无标题的单选实例 schemaEnumSchema验证带标题的单选实例 schema- 无标题的单选实例 schema 验证正确的单选值
- 无标题的单选实例 schema 判定错误的单选值无效
- 带标题的单选实例 schema 验证正确的单选值
- 带标题的单选实例 schema 判定错误的单选值无效
多选
EnumSchema验证无标题的多选实例 schemaEnumSchema验证带标题的多选实例 schema- 无标题的多选实例 schema 验证正确的多选值 无标题的多选实例 schema 验证并判定错误的多选值 带标题的多选实例 schema 验证正确的多选值 带标题的多选实例 schema 验证并判定错误的多选值
JSON meta-schema
以下是我们建议在规范的 schema.json 中替换当前 EnumSchema 的方案。
{
"$schema": "https://json-schema.org/draft-07/schema",
"definitions": {
// New Definitions Follow
"UntitledSingleSelectEnumSchema": {
"type": "object",
"properties": {
"type": { "const": "string" },
"title": { "type": "string" },
"description": { "type": "string" },
"enum": {
"type": "array",
"items": { "type": "string" },
"minItems": 1
}
},
"required": ["type", "enum"],
"additionalProperties": false
},
"UntitledMultiSelectEnumSchema": {
"type": "object",
"properties": {
"type": { "const": "array" },
"title": { "type": "string" },
"description": { "type": "string" },
"minItems": {
"type": "number",
"minimum": 0
},
"maxItems": {
"type": "number",
"minimum": 0
},
"items": {
"type": "object",
"properties": {
"type": { "const": "string" },
"enum": {
"type": "array",
"items": { "type": "string" },
"minItems": 1
}
},
"required": ["type", "enum"],
"additionalProperties": false
}
},
"required": ["type", "items"],
"additionalProperties": false
},
"TitledSingleSelectEnumSchema": {
"type": "object",
"required": ["type", "anyOf"],
"properties": {
"type": { "const": "string" },
"title": { "type": "string" },
"description": { "type": "string" },
"anyOf": {
"type": "array",
"items": {
"type": "object",
"required": ["const", "title"],
"properties": {
"const": { "type": "string" },
"title": { "type": "string" }
},
"additionalProperties": false
}
}
},
"additionalProperties": false
},
"TitledMultiSelectEnumSchema": {
"type": "object",
"required": ["type", "anyOf"],
"properties": {
"type": { "const": "array" },
"title": { "type": "string" },
"description": { "type": "string" },
"anyOf": {
"type": "array",
"items": {
"type": "object",
"required": ["const", "title"],
"properties": {
"const": { "type": "string" },
"title": { "type": "string" }
},
"additionalProperties": false
}
}
},
"additionalProperties": false
},
"LegacyEnumSchema": {
"properties": {
"type": {
"type": "string",
"const": "string"
},
"title": { "type": "string" },
"description": { "type": "string" },
"enum": {
"type": "array",
"items": { "type": "string" }
},
"enumNames": {
"type": "array",
"items": { "type": "string" }
}
},
"required": ["enum", "type"],
"type": "object"
},
"EnumSchema": {
"oneOf": [
{ "$ref": "#/definitions/UntitledSingleSelectEnumSchema" },
{ "$ref": "#/definitions/UntitledMultiSelectEnumSchema" },
{ "$ref": "#/definitions/TitledSingleSelectEnumSchema" },
{ "$ref": "#/definitions/TitledMultiSelectEnumSchema" },
{ "$ref": "#/definitions/LegacyEnumSchema" }
]
}
}
}