> ## Documentation Index
> Fetch the complete documentation index at: https://mcp.zhcndoc.com/llms.txt
> Use this file to discover all available pages before exploring further.

# SEP-1330: 询问枚举模式改进与标准合规性

> 询问枚举模式改进与标准合规性

<div className="flex items-center gap-2 mb-4">
  <Badge color="green" shape="pill">
    最终版
  </Badge>

  <Badge color="gray" shape="pill">
    标准轨道
  </Badge>
</div>

| 字段       | 值                                                                               |
| -------- | ------------------------------------------------------------------------------- |
| **SEP**  | 1330                                                                            |
| **标题**   | 询问枚举模式改进与标准合规性                                                                  |
| **状态**   | 最终版                                                                             |
| **类型**   | 标准轨道                                                                            |
| **创建日期** | 2025-08-11                                                                      |
| **作者**   | chughtapan                                                                      |
| **赞助人**  | 无                                                                               |
| **PR**   | [#1330](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/1330) |

***

## 摘要

本 SEP 提议改进 MCP 中的枚举模式定义，弃用非标准的 `enumNames` 属性，转而采用符合 JSON 模式的模式，并除了单选模式外，引入对多选枚举模式的额外支持。新模式已针对 JSON 规范进行了验证。

**模式变更：** [https://github.com/modelcontextprotocol/modelcontextprotocol/pull/1148](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/1148)
Typescript SDK 变更：[https://github.com/modelcontextprotocol/typescript-sdk/pull/1077](https://github.com/modelcontextprotocol/typescript-sdk/pull/1077)
Python SDK 变更：[https://github.com/modelcontextprotocol/python-sdk/pull/1246](https://github.com/modelcontextprotocol/python-sdk/pull/1246)
**客户端实现：** [https://github.com/evalstate/fast-agent/pull/324/files](https://github.com/evalstate/fast-agent/pull/324/files)
**工作演示：** [https://asciinema.org/a/anBvJdqEmTjw0JkKYOooQa5Ta](https://asciinema.org/a/anBvJdqEmTjw0JkKYOooQa5Ta)

## Motivation

The existing enum pattern uses a non-standard method to add titles to enum values. It also restricts the use of enums in elicitation (Elicitation) (and any future schema objects that should adopt `EnumSchema`) to single-choice models only. Requiring users to select multiple items is a common pattern. In UI terms, this corresponds to the difference between using checkboxes and radio buttons.

For these reasons, we propose the following non-breaking minor improvements to `EnumSchema` to improve the user and developer experience.

* Keep the existing `EnumSchema` as “legacy”
  * It uses a non-standard method to add titles to enum values
  * Mark it as legacy, but continue supporting it for now.
  * According to @dsp-ant, when we have a proper deprecation strategy, we will mark it as deprecated
* Introduce the distinction between untitled enums and titled enums.
  * If the enum values are sufficient, there is no need to specify a separate title for each value.
  * If the enum values are not suitable for display, a title can be specified for each value.
* Introduce the distinction between single-select and multi-select enums.
  * If only one value can be selected, single-select mode can be used
  * If multiple values can be selected, multi-select mode can be used
* In `ElicitResponse`, add arrays as an `additionalProperty` type
  * Allows selections of multiple enum values to be returned to the server

## 规范

### 1. 将带有非标准 `enumNames` 属性的当前 `EnumSchema` 标记为“遗留”

当前的 MCP 规范使用非标准的 `enumNames` 属性来提供枚举值的显示名称。我们提议将 `enumNames` 属性标记为遗留，建议使用 `TitledSingleSelectEnum`，这是我们下面定义的一种符合标准的枚举类型。

```typescript theme={null}
// 继续支持当前的 EnumSchema 作为遗留版本

/**
 * 遗留：请改用 TitledSingleSelectEnumSchema。
 * 此接口将在未来版本中移除。
 */
export interface LegacyEnumSchema {
  type: "string";
  title?: string;
  description?: string;
  enum: string[];
  enumNames?: string[]; // 枚举值的标题（非标准，遗留）
}
```

### 2. 定义单选枚举（带有有标题和无标题变体）

枚举可能需要也可能不需要标题。枚举值可能是人类可读的并且适合显示。在这种情况下，使用 JSON 模式关键字 `enum` 的无标题实现更简单。添加标题需要使用 `const` 和 `title` 将 `enum` 数组替换为对象数组。

```typescript theme={null}
// 不带标题的单选枚举
export type UntitledSingleSelectEnumSchema = {
  type: "string";
  title?: string;
  description?: string;
  enum: string[]; // 不带标题的普通枚举
};

// 带标题的单选枚举
export type TitledSingleSelectEnumSchema = {
  type: "string";
  title?: string;
  description?: string;
  oneOf: Array<{
    const: string; // 枚举值
    title: string; // 枚举值的显示名称
  }>;
};

// 组合单选枚举
export type SingleSelectEnumSchema =
  | UntitledSingleSelectEnumSchema
  | TitledSingleSelectEnumSchema;
```

### 3. 引入多选枚举（带有有标题和无标题变体）

虽然询问不支持任意 JSON 类型（如数组和对象），以便客户端可以轻松显示选择项，但可以轻松实现多选枚举。

```typescript theme={null}
// 不带标题的多选枚举
export type UntitledMultiSelectEnumSchema = {
  type: "array";
  title?: string;
  description?: string;
  minItems?: number; // 要选择的最小项目数
  maxItems?: number; // 要选择的最大项目数
  items: {
    type: "string";
    enum: string[]; // 不带标题的普通枚举
  };
};

// 带标题的多选枚举
export type TitledMultiSelectEnumSchema = {
  type: "array";
  title?: string;
  description?: string;
  minItems?: number; // 要选择的最小项目数
  maxItems?: number; // 要选择的最大项目数
  items: {
    oneOf: Array<{
      const: string; // 枚举值
      title: string; // 枚举值的显示名称
    }>;
  };
};

// 组合多选枚举
export type MultiSelectEnumSchema =
  | UntitledMultiSelectEnumSchema
  | TitledMultiSelectEnumSchema;
```

### 4. 将所有变体组合为 `EnumSchema`

最终的 `EnumSchema` 将遗留、多选和单选模式汇总为一个，定义如下：

```typescript theme={null}
// 组合遗留、多选和单选枚举
export type EnumSchema =
  | SingleSelectEnumSchema
  | MultiSelectEnumSchema
  | LegacyEnumSchema;
```

### 5. 扩展 ElicitResult

当前的询问结果模式仅允许返回原始类型。我们扩展此内容以包含用于 MultiSelectEnums 的字符串数组：

```typescript theme={null}
export interface ElicitResult extends Result {
  action: "accept" | "decline" | "cancel";
  content?: { [key: string]: string | number | boolean | string[] }; // string[] 是新增的
}
```

## 实例模式示例

### 不带标题的单选（无变更）

```json theme={null}
{
  "type": "string",
  "title": "颜色选择",
  "description": "选择你最喜欢的颜色",
  "enum": ["Red", "Green", "Blue"],
  "default": "Green"
}
```

### 带标题的遗留单选

```json theme={null}
{
  "type": "string",
  "title": "颜色选择",
  "description": "选择你最喜欢的颜色",
  "enum": ["#FF0000", "#00FF00", "#0000FF"],
  "enumNames": ["红色", "绿色", "蓝色"],
  "default": "Green"
}
```

### 带标题的单选

```json theme={null}
{
  "type": "string",
  "title": "颜色选择",
  "description": "选择你最喜欢的颜色",
  "oneOf": [
    { "const": "#FF0000", "title": "红色" },
    { "const": "#00FF00", "title": "绿色" },
    { "const": "#0000FF", "title": "蓝色" }
  ],
  "default": "#00FF00"
}
```

### 不带标题的多选

```json theme={null}
{
  "type": "array",
  "title": "颜色选择",
  "description": "选择你最喜欢的颜色",
  "minItems": 1,
  "maxItems": 3,
  "items": {
    "type": "string",
    "enum": ["Red", "Green", "Blue"]
  },
  "default": ["Green"]
}
```

### 带标题的多选

```json theme={null}
{
  "type": "array",
  "title": "颜色选择",
  "description": "选择你最喜欢的颜色",
  "minItems": 1,
  "maxItems": 3,
  "items": {
    "anyOf": [
      { "const": "#FF0000", "title": "红色" },
      { "const": "#00FF00", "title": "绿色" },
      { "const": "#0000FF", "title": "蓝色" }
    ]
  },
  "default": ["Green"]
}
```

## 理由

1. **标准合规性**：与官方 JSON 模式规范保持一致。标准模式可与现有的 JSON 模式验证器一起工作
2. **灵活性**：支持普通枚举和带有显示名称的枚举，适用于单选和多选枚举。
3. **客户端实现：** 表明实现一组复选框与单个复选框的额外开销是最小的：[https://github.com/evalstate/fast-agent/pull/324/files](https://github.com/evalstate/fast-agent/pull/324/files)

## 向后兼容性

`LegacyEnumSchema` 类型在迁移期间保持向后兼容。使用 `enumNames` 的现有实现将继续工作，直到实施协议范围的弃用策略，并且此模式被移除。

## 参考实现

**模式变更：** [https://github.com/modelcontextprotocol/modelcontextprotocol/pull/1148](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/1148)
Typescript SDK 变更：[https://github.com/modelcontextprotocol/typescript-sdk/pull/1077](https://github.com/modelcontextprotocol/typescript-sdk/pull/1077)
Python SDK 变更：[https://github.com/modelcontextprotocol/python-sdk/pull/1246](https://github.com/modelcontextprotocol/python-sdk/pull/1246)
**客户端实现：** [https://github.com/evalstate/fast-agent/pull/324/files](https://github.com/evalstate/fast-agent/pull/324/files)
**工作演示：** [https://asciinema.org/a/anBvJdqEmTjw0JkKYOooQa5Ta](https://asciinema.org/a/anBvJdqEmTjw0JkKYOooQa5Ta)

## 安全考虑

未发现安全隐患。此变更纯粹是关于模式结构和标准合规性。

## 附录

### 验证

使用存储在 [https://www.jsonschemavalidator.net/](https://www.jsonschemavalidator.net/) 的 JSON Schema Validator 中的验证，我们验证：

* 本文档中的所有示例实例模式针对下一节中提出的 JSON 元模式 `EnumSchema` 进行验证。
* 针对本文档中的示例实例模式验证有效和无效的值。

#### 遗留单选

* `EnumSchema` 验证一个 [带有标题的遗留单选实例模式](https://www.jsonschemavalidator.net/s/lsK7Bn0C)
* 遗留带标题单选实例模式验证 [一个正确的单选](https://www.jsonschemavalidator.net/s/GSk7rnRe)
* 遗留带标题单选实例模式验证 [一个错误的单选](https://www.jsonschemavalidator.net/s/3kYvxsVP)

#### 单选

* `EnumSchema` 验证一个 [无标题的单选实例模式](https://www.jsonschemavalidator.net/s/MBlHW5IQ)
* `EnumSchema` 验证一个 [带有标题的单选实例模式](https://www.jsonschemavalidator.net/s/s38xt4JV)
* 无标题单选实例模式验证 [一个正确的单选](https://www.jsonschemavalidator.net/s/M0hkYoeG)
* 无标题单选实例模式使 \[一个错误的单选] 无效 ([https://www.jsonschemavalidator.net/s/3Try4BCt](https://www.jsonschemavalidator.net/s/3Try4BCt))
* 带标题单选实例模式验证 [一个正确的单选](https://www.jsonschemavalidator.net/s/4oDbv9yt)
* 带标题单选实例模式使 \[一个错误的单选] 无效 ([https://www.jsonschemavalidator.net/s/A2KlNzLH](https://www.jsonschemavalidator.net/s/A2KlNzLH))

#### 多选

* `EnumSchema` 验证 [无标题的多选实例模式](https://www.jsonschemavalidator.net/s/4uc3Ndsq)
* `EnumSchema` 验证 [带有标题的多选实例模式](https://www.jsonschemavalidator.net/s/TmkIqqXI)
* 无标题多选实例模式验证 [一个正确的多选](https://www.jsonschemavalidator.net/s/IE8Bkvtg)
  无标题多选实例模式验证使 \[ 一个错误的多选] 无效 ([https://www.jsonschemavalidator.net/s/8tlqjUgW](https://www.jsonschemavalidator.net/s/8tlqjUgW))
  带标题多选实例模式验证 [一个正确的多选](https://www.jsonschemavalidator.net/s/Nb1Rw1qa)
  带标题多选实例模式验证使 \[一个错误的多选] 无效 ([https://www.jsonschemavalidator.net/s/MRfyqrVC](https://www.jsonschemavalidator.net/s/MRfyqrVC))

### JSON 元模式

这是我们提出的用于替换规范中 `schema.json` 内当前 `EnumSchema` 的方案。

```json theme={null}
{
  "$schema": "https://json-schema.org/draft-07/schema",
  "definitions": {
    // 新定义如下
    "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" }
      ]
    }
  }
}
```
