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

# SEP-2106：工具的 inputSchema 与 outputSchema 符合 JSON Schema 2020-12

* **状态（Status）**: Final
* **类型（Type）**: Standards Track
* **创建（Created）**: 2026-01-06
* **作者（Author(s)）**: John McBride (@jpmcb) —— 原始提案；Ola Hungerford (@olaservo) —— 当前引导者，SEP-1850 转换后
* **担保人（Sponsor）**: Ola Hungerford (@olaservo)
* **PR**: [https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2106](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2106)

> **作者身份说明：** 原始提案由 John McBride (@jpmcb) 在 [PR #881](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/881) 中撰写，早于 [SEP-1850](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/seps/1850-pr-based-sep-workflow.md) 基于 PR 的工作流。本文件将该提案转换为当前 SEP 格式，并由 Ola Hungerford (@olaservo) 引导，她还根据评审反馈修订了向后兼容性、安全影响和 SDK 迁移章节。原始文字和设计意图仍属 John；转换以来的实质性变更在本 PR 的提交历史中跟踪。

## 摘要

本 SEP 提议放宽对 `inputSchema`、`outputSchema` 和 `structuredContent` 的限制，以更好地支持 JSON Schema 2020-12。具体而言：

* **`inputSchema`**：保留 `type: "object"` 为必需（因为工具参数是对象），但允许任何额外的 JSON Schema 属性，以支持强大的校验组合（`anyOf`、`oneOf`、`allOf` 等）
* **`outputSchema`**：完全支持 JSON Schema 2020-12，因为 MCP 服务器可能返回任何有效的 JSON
* **`structuredContent`**：接受任何经 `outputSchema` 校验的 JSON 值

本提案使 MCP 服务器能够利用 JSON Schema 2020-12 的表达力，同时保持与既有实现的向后兼容。

## 动机

当前的 MCP 规范以与完整 JSON Schema 支持相冲突的方式限制了工具 schema：

1. **inputSchema 限制**：当前只允许 `type`、`properties` 和 `required` 字段。这妨碍了使用 `anyOf`、`oneOf` 和 `allOf` 等组合关键字来实现复杂的对象校验模式。

2. **outputSchema 限制**：同样被限制为带 `properties` 和 `required` 的 `type: "object"`，尽管规范声称支持"JSON Schema"。

3. **structuredContent 限制**：被定义为 `{ [key: string]: unknown }`（一个带字符串键的对象），这妨碍了返回数组——一种常见的 API 响应模式。

### 现实世界的影响

设想一个返回逐小时预报的天气 API 工具：

```json theme={null}
[
  { "hour": "09:00", "temp": 68, "conditions": "sunny" },
  { "hour": "10:00", "temp": 72, "conditions": "partly cloudy" },
  { "hour": "11:00", "temp": 75, "conditions": "cloudy" }
]
```

目前，这个自然的数组响应是**不可能的**，因为 `structuredContent` 必须是对象。开发者被迫将数组包裹在不必要的容器对象中：

```json theme={null}
{
  "forecasts": [
    { "hour": "09:00", "temp": 68, "conditions": "sunny" },
    ...
  ]
}
```

这种人为约束：

* 为响应添加了不必要的嵌套
* 与常见的 REST API 模式相冲突
* 妨碍了对数组响应的直接 schema 校验

### Schema 组合用例

当前的 `inputSchema` 限制妨碍了合理的 schema 模式。有了本 SEP，工具可以在 `type: "object"` 旁使用组合关键字：

```json theme={null}
{
  "type": "object",
  "oneOf": [
    { "properties": { "id": { "type": "string" } }, "required": ["id"] },
    { "properties": { "name": { "type": "string" } }, "required": ["name"] }
  ]
}
```

此模式允许工具接受基于 ID 或基于名称的查找——一种常见的 API 设计，而它目前不受支持，因为 schema 只允许 `type`、`properties` 和 `required` 字段。

## 规范

### 1. 放宽 inputSchema

**当前定义：**

```typescript theme={null}
inputSchema: {
  type: "object";
  properties?: { [key: string]: object };
  required?: string[];
};
```

**提议的定义：**

```typescript theme={null}
inputSchema: {
  $schema?: string;
  type: "object";
  [key: string]: unknown;
};
```

`inputSchema` 字段保留 `type: "object"` 要求（因为工具参数始终是对象），但现在接受任何额外的 JSON Schema 属性。这支持：

* 组合关键字：`anyOf`、`oneOf`、`allOf`、`not`
* 条件 schema：`if`/`then`/`else`
* 引用 schema：`$ref`、`$defs`
* 任何其他有效的 JSON Schema 2020-12 关键字

### 2. 放宽 outputSchema

**当前定义：**

```typescript theme={null}
outputSchema?: {
  type: "object";
  properties?: { [key: string]: object };
  required?: string[];
};
```

**提议的定义：**

```typescript theme={null}
outputSchema?: {
  $schema?: string;
  [key: string]: unknown;
};
```

`outputSchema` 字段接受任何有效的 JSON Schema 2020-12 对象，支持校验数组、基本类型或复杂组合的 schema。与 `inputSchema` 不同，没有 `type: "object"` 要求，因为工具输出可以是任何有效的 JSON。

### 3. 放宽 structuredContent

**当前定义：**

```typescript theme={null}
structuredContent?: { [key: string]: unknown };
```

**提议的定义：**

```typescript theme={null}
structuredContent?: unknown;
```

`structuredContent` 字段接受任何符合工具 `outputSchema` 的有效 JSON 值。这包括：

* 对象：`{ "key": "value" }`
* 数组：`[1, 2, 3]` 或 `[{ "id": "abc" }, { "id": "xyz" }]`
* 基本类型：`"string"`、`42`、`true`、`null`

### 4. 文档更新

更新 `docs/specification/draft/server/tools.mdx`：

* 移除关于 `structuredContent`"作为 JSON 对象返回"的陈述
* 澄清 `structuredContent` 可以是符合 `outputSchema` 的任何 JSON 值
* 添加演示数组响应的示例

### 5. 示例

#### 返回对象数组的工具：

```json theme={null}
{
  "name": "list_users",
  "description": "List all users in the system",
  "inputSchema": {
    "type": "object",
    "properties": {
      "limit": { "type": "integer", "minimum": 1, "maximum": 100 }
    }
  },
  "outputSchema": {
    "type": "array",
    "items": {
      "type": "object",
      "properties": {
        "id": { "type": "string" },
        "name": { "type": "string" },
        "email": { "type": "string", "format": "email" }
      },
      "required": ["id", "name"]
    }
  }
}
```

响应：

```json theme={null}
{
  "content": [
    {
      "type": "text",
      "text": "Found 2 users: Alice (u1, alice@example.com) and Bob (u2, bob@example.com)."
    }
  ],
  "structuredContent": [
    { "id": "u1", "name": "Alice", "email": "alice@example.com" },
    { "id": "u2", "name": "Bob", "email": "bob@example.com" }
  ]
}
```

#### 带组合 schema 的工具：

```json theme={null}
{
  "name": "find_resource",
  "description": "Find a resource by ID or name",
  "inputSchema": {
    "type": "object",
    "oneOf": [
      {
        "properties": { "id": { "type": "string", "format": "uuid" } },
        "required": ["id"]
      },
      {
        "properties": { "name": { "type": "string", "minLength": 1 } },
        "required": ["name"]
      }
    ]
  }
}
```

## 理由

### 为何不只是允许数组？

虽然我们可以简单地扩展 `structuredContent` 以允许数组，但这将是一个不完整的解决方案。根本原因是 schema 类型被人为地限制为 `type: "object"`。通过允许任何有效的 JSON Schema，我们：

1. 支持 JSON Schema 2020-12 的全部能力
2. 与规范对 JSON Schema 支持的声称对齐
3. 提供一致、有原则的方式，而非零敲碎打的修补

### 为何不要求封装对象？

曾考虑要求将数组包裹在对象中（例如 `{ "items": [...] }`），但被否决，因为：

1. 它为响应增加了不必要的复杂性
2. 它与常见的 API 设计模式相冲突
3. 它妨碍了对实际响应结构的直接 schema 校验
4. JSON Schema 已经优雅地处理数组校验

### 现实世界的 API 模式

许多生产 API 直接返回数组：

* **GitHub Events API**：返回事件对象数组
* **AccuWeather Search API**：返回位置匹配数组
* **REST 集合端点**：标准的 `GET /users` 返回 `[{...}, {...}]`

强制封装对象给将既有 API 与 MCP 集成的开发者制造了摩擦。通用的 JSON Schema 校验库应无需 MCP 特定定制即可工作。

### 与 JSON Schema 2020-12 对齐

JSON Schema 2020-12 为 schema 组合和校验提供了强大的特性。通过移除人为限制，MCP 与行业标准对齐（OpenAPI 3.1 使用 JSON Schema 2020-12），并使开发者能够利用既有的 JSON Schema 知识和工具。

### SDK 生态证据

当前限制造成的摩擦并非理论上的。FastMCP，最流行的 MCP Python SDK 之一，已实现了大量变通办法：

1. **显式错误消息**承认这一局限：

   ```python theme={null}
   raise ValueError(
       f"Output schemas must represent object types due to MCP spec limitations."
   )
   ```

2. **自动封装基础设施**增加了复杂性：
   * 一个 `_WrappedResult` dataclass 封装非对象返回
   * 一个自定义的 `x-fastmcp-wrap-result` 扩展支持客户端侧解封装
   * SDK 和客户端都需要匹配的封装/解封装逻辑

3. 这些变通办法已导致**真实缺陷**：
   * Issue #2455：没有 `type: object` 的 `$ref` schema 破坏了服务器上的所有工具
   * Issue #2421：意外的 `{"result": ...}` 封装令用户困惑

这表明当前限制制造了真实的生态摩擦，而 SEP-2106 将消除它。

### OpenAPI 先例

OpenAPI 规范经历了类似的演进。OpenAPI 3.0 使用 JSON Schema 的一个"扩展子集"，带有自定义限制（例如要求 `nullable: true` 而非允许 `"null"` 作为一种类型）。

OpenAPI 3.1 作出了完全与 JSON Schema 2020-12 对齐的战略决定，接受破坏性变更以消除摩擦。结果：更好的工具兼容性和更少的生态困惑。

| OpenAPI 的问题        | MCP 的对应                      |
| ------------------ | ---------------------------- |
| `type` 必须是字符串而非数组  | `inputSchema` 只允许特定字段        |
| 无法使用标准的 null 处理    | schema 中无法使用 `oneOf`/`anyOf` |
| 自定义 `nullable` 关键字 | 仅限对象的 `structuredContent`    |
| 造成工具困惑             | 造成 SDK 变通办法                  |

MCP 可以从 OpenAPI 的经验中学习，而非在数年间重复相同的演进。

## 向后兼容性

此变更**线路格式向后兼容**，但根据版本不匹配的方向存在细微差别。

### 兼容性矩阵

|                 | 新客户端（SEP 后）              | 旧客户端（SEP 前）                                                                 |
| --------------- | ------------------------ | --------------------------------------------------------------------------- |
| **新服务器（SEP 后）** | 完全兼容。                    | **仅当服务器返回对象类型的 `structuredContent` 时**兼容。`structuredContent` 中的数组/基本类型可能破坏。 |
| **旧服务器（SEP 前）** | 完全兼容。既有的仅对象 schema 保持有效。 | 不变。                                                                         |

不对称性：利用数组或基本类型 `structuredContent`（或 `inputSchema` 中组合关键字）的新服务器，不能假定旧客户端会接受该响应。针对旧线路格式编写的旧客户端可能拒绝非 JSON 对象的 `structuredContent`，或无法校验包含 `type`/`properties`/`required` 之外关键字的 `inputSchema`。

为与较旧客户端保持互操作，**使用数组或基本类型 `structuredContent` 的服务器还必须（MUST）发出一个包含序列化 JSON 的 `TextContent` 块**（如工具规范中已建议的）。不理解非对象 `structuredContent` 的客户端可以回退到文本内容。

### TypeScript / SDK 迁移

将 `structuredContent` 字段类型从 `{ [key: string]: unknown }` 拓宽为 `unknown`，对于类型化的消费者而言是一个**源码破坏性变更**，即便线路格式不变。诸如以下代码：

```typescript theme={null}
const result = await client.callTool({ name: "get_weather", arguments: { ... } });
const temp = result.structuredContent?.temperature;        // previously compiled (type: unknown)
const city = result.structuredContent?.["city"] as string; // previously compiled
```

在变更后将不再通过类型检查，因为 TypeScript 禁止在没有收窄守卫的情况下对 `unknown` 进行属性访问：

```typescript theme={null}
const sc = result.structuredContent;
if (sc && typeof sc === "object" && !Array.isArray(sc)) {
  const temp = (sc as Record<string, unknown>).temperature;
}
```

这一破坏是有意的——每当工具返回非对象时，先前的类型就是个谎言——但 SDK 维护者**应当（SHOULD）**：

* 在 SDK 发布说明中记录该迁移。
* 在符合工效学之处，提供类型化辅助工具（例如对工具 `outputSchema` 的泛型），使消费者无需手写收窄守卫。

### 迁移路径

* **服务器**：无需迁移即可像以前一样工作。要使用数组或基本类型 `structuredContent`，还需发出一个序列化的 `TextContent` 回退。
* **客户端**：旧客户端继续针对仅对象服务器工作。要消费新的灵活性，应在 `structuredContent` 中接受任何 JSON 值，并在存在 `outputSchema` 时对照校验。
* **SDK**：更新生成的类型以反映新 schema（`structuredContent` 为 `unknown`，开放式的 `inputSchema`/`outputSchema`），并在发布说明中指出源码破坏性的类型变更。

## 安全影响

JSON Schema 校验已经处理类型检查、值约束和必填字段校验，实现\*\*必须（MUST）\*\*继续对照声明的 schema 校验所有输入和输出。允许完整的 JSON Schema 2020-12 词汇表带来两个值得明确指导的方面。

### `$ref` 解引用（SSRF 与 Fetch-DoS）

JSON Schema 2020-12 允许 `$ref` 指向绝对 URI，而不仅是同一文档中的 JSON 指针。天真地对遇到的每个 `$ref` 发起 HTTP 请求来解析的实现，给了攻击者一个服务器端请求伪造/获取放大的原语：恶意的工具定义可以致使宿主获取任意 URL，包括内部元数据端点或旨在耗尽资源的大型载荷。

为缓解这一点：

* 实现\*\*不得（MUST NOT）\*\*自动解引用解析为网络 URI 的 `$ref` 值（即任何不是同文档 JSON 指针，如 `#/$defs/Foo` 或内部 `$anchor` 的东西）。
* 此处"自动"意为"作为正常校验或 schema 处理的一部分，无需显式的操作员动作"。实现\*\*可以（MAY）**提供一个获取非本地 `$ref` 的选择加入模式，但它必须默认禁用，并**应当（SHOULD）\*\*强制执行主机允许列表（或至少拒绝环回、链路本地和私有网络地址）、应用超时和大小限制，并记录解引用的 URI。
* 因未解析的外部 `$ref` 而校验失败的 schema \*\*应当（SHOULD）\*\*被拒绝，而非默默地被当作宽松处理。

### 组合关键字的资源使用

组合关键字（`anyOf`、`oneOf`、`allOf`、`if`/`then`/`else`）和 `$defs` 支持有表达力的 schema，但病态组合校验起来可能代价高昂。实现\*\*应当（SHOULD）\*\*应用合理的界限——例如最大 schema 深度、子 schema 总数的上限，或每次校验的时间预算——以防止恶意的工具定义充当针对校验器的 CPU DoS 向量。

## 参考实现

### TypeScript SDK

一个演示放宽后类型限制的参考实现：

* **分支**：[olaservo/typescript-sdk@sep-834-v1x](https://github.com/olaservo/typescript-sdk/tree/sep-834-v1x)
* **npm**：`@olaservo/mcp-sdk@1.25.2-sep834.4`
* **关键变更**：
  * `inputSchema`：保留 `type: "object"` 但允许任何额外的 JSON Schema 属性（如 `oneOf`/`anyOf` 组合）
  * `outputSchema`：任何有效的 JSON Schema 对象（数组、基本类型、对象、组合）
  * `structuredContent`：任何 JSON 值（对象、数组或基本类型）
  * McpServer 高层 API 更新以支持数组和基本类型 outputSchema

### Everything Server 演示工具

向 `everything` 服务器添加了三个演示工具，展示 SEP-2106 能力：

* **分支**：[olaservo/servers@sep-834-json-schema-2020-12](https://github.com/olaservo/servers/tree/sep-834-json-schema-2020-12/src/everything)
* **npm**：`@olaservo/mcp-server-everything-sep834@1.1.0-sep834.1`
* **工具**：
  * `get-weather-forecast`：直接在 `structuredContent` 中返回逐小时预报的**原始数组**
    * 与 SEP-2106 动机章节中的确切示例匹配
    * `outputSchema`：`z.array(HourlyForecastSchema)` —— 根为数组类型
    * `structuredContent`：`[{hour, temp, conditions}, ...]` —— 直接数组
  * `find-by-id-or-name`：演示灵活的输入模式（接受 `id` 或 `name`）
  * `get-count`：直接在 `structuredContent` 中返回**原始数字**（不封装在对象中）
    * `outputSchema`：`z.number()` —— 根为基本类型
    * `structuredContent`：`42` —— 直接基本类型

### 相关链接

* 原始 PR：[https://github.com/modelcontextprotocol/modelcontextprotocol/pull/881](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/881)
* 相关 issue：[https://github.com/modelcontextprotocol/modelcontextprotocol/issues/834](https://github.com/modelcontextprotocol/modelcontextprotocol/issues/834)
* `outputSchema` 类型限制不一致：[https://github.com/modelcontextprotocol/modelcontextprotocol/issues/1906](https://github.com/modelcontextprotocol/modelcontextprotocol/issues/1906)
* TypeScript SDK schema 类型：[https://github.com/modelcontextprotocol/typescript-sdk/issues/1149](https://github.com/modelcontextprotocol/typescript-sdk/issues/1149)
* SEP-2200（澄清工具结果内容与模型可见性）：[https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2200](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2200)

### 实现指南

SDK 实现将需要：

1. 更新 `inputSchema` 类型，保留 `type: "object"` 但允许任何额外的 JSON Schema 属性
2. 更新 `outputSchema` 类型，允许任何有效的 JSON Schema（移除 `type: "object"` 约束）
3. 更新 `structuredContent` 类型，接受任何有效的 JSON 值
4. 相应地更新 JSON Schema 定义

## 致谢

本提案建立在 GitHub issue #834 的讨论之上，并纳入了来自 MCP 社区的反馈。
