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

# 缓存

<div id="enable-section-numbers" />

Model Context Protocol (MCP) 支持对部分结果进行缓存。这允许客户端缓存响应，并减少不必要的重复获取。缓存与[变化通知](#interaction-with-notifications)互为补充，二者可以共存。

## 可缓存结果

服务器 MUST 在以下操作返回的结果中包含缓存提示：

* `server/discover`
* `tools/list`
* `prompts/list`
* `resources/list`
* `resources/templates/list`
* `resources/read`

## 缓存模型

MCP 中的可缓存结果使用两个字段向客户端提供缓存提示：

* <b>生存时间 (TTL) 字段</b> `ttlMs` 是以毫秒为单位的整数值，指定客户端 MAY 将结果视为新鲜的时长。
* <b>缓存作用域字段</b> `cacheScope` 指示缓存响应的预期作用域，可以是 `"public"` 或 `"private"`。

### Time-to-Live (TTL) 字段

`ttlMs` 字段是服务器给出的提示，表示客户端 MAY 将结果视为新鲜的毫秒数。其语义类似于 HTTP `Cache-Control: max-age`。

* 如果 `ttlMs` 为 `0`，响应 **SHOULD** 被视为立即过期。客户端 MAY 在每次需要结果时重新获取。
* 如果 `ttlMs` 为正数，客户端 **SHOULD** 在收到响应后的相应毫秒数内将结果视为新鲜。
* 如果缺少 `ttlMs`，客户端 **SHOULD** 假定默认值为 `0`（立即过期），并依赖自身缓存启发式规则或通知。这只应出现在较旧的服务器版本中。
* 如果 `ttlMs` 为负数，客户端 **SHOULD** 忽略它并将其视为 `0`。

服务器 **MUST** 提供 `>= 0` 的 `ttlMs` 值。

<Note>
  TTL 是**新鲜度提示**，不是保证。服务器 MAY 在 TTL 过期前更改底层数据。
  TTL 告诉客户端它可以合理地避免重新获取多久，而不是保证数据会保持不变多久。
</Note>

#### 新鲜度计算

客户端记录收到响应时的本地时间（`t_received`）。当满足以下条件时，响应被视为**新鲜**：

```
now < t_received + ttlMs
```

一旦 TTL 过期，响应即为**过期**，客户端 **SHOULD** 在下次访问时重新获取。

客户端 **SHOULD NOT** 将 TTL 视为触发自动后台重新获取的轮询间隔。TTL 是新鲜度提示：客户端在需要数据时检查新鲜度，并且仅在过期时重新获取。选择轮询的实现 **MUST** 应用抖动和退避。

如果客户端有理由相信数据已发生变化（例如，在工具调用上收到意外错误，表明方法未找到或参数无效），客户端 **MAY** 在 TTL 过期前重新获取。

如果重新获取期间发生错误（例如网络问题、服务器停机），客户端 **MAY** 提供过期响应。

### 缓存作用域字段

`cacheScope` 字段控制谁可以缓存响应，类似于 HTTP `Cache-Control: public` 与 `Cache-Control: private`。

| 值           | 含义                                                                                      |
| ----------- | --------------------------------------------------------------------------------------- |
| `"public"`  | 响应不包含用户特定数据。任何客户端、共享网关或缓存代理 **MAY** 存储并向任何用户提供该缓存响应。                                    |
| `"private"` | 响应包含不应在调用方之间共享的私有数据。缓存响应 **MAY** 在相同授权上下文中复用。缓存 **MUST NOT** 跨授权上下文共享（例如，不同访问令牌需要不同缓存）。 |

#### 选择缓存作用域

* 当工具、提示和资源模板列表对所有用户都相同时，**`"public"`** 是合适的。
* 对于依赖已认证用户的 `resources/read` 结果，或按用户变化的过滤列表结果，**`"private"`** 是合适的。

<a id="interaction-with-notifications" />

## 与通知的交互

TTL 与服务器推送通知互为补充：

* 服务器 **MAY** 在其能力中不公告 `listChanged: true` 的情况下提供 `ttlMs`。在这种情况下，客户端完全依赖基于 TTL 的新鲜度。
* 服务器 **MAY** 同时公告 `listChanged: true` 并提供 `ttlMs`。在这种情况下，客户端可以使用 TTL 来避免在通知之间进行不必要的重新获取，而通知则作为立即失效信号。

当缓存响应仍然新鲜时收到相关通知，该通知会使缓存响应**失效**，并应将其视为立即过期。

```mermaid theme={null}
sequenceDiagram
    participant Client
    participant Server

    Client->>Server: tools/list
    Server-->>Client: { tools: [...], ttlMs: 300000 }
    Note over Client: Cache response, fresh for 5 min

    Note over Client: 2 minutes later...
    Client->>Client: Need tools list → cache still fresh, use cached

    Note over Client: 3 minutes later (TTL expired)...
    Client->>Client: Need tools list → cache stale
    Client->>Server: tools/list
    Server-->>Client: { tools: [...], ttlMs: 300000 }

    Note over Server: Tools change before TTL expires
    Server-->>Client: notifications/tools/list_changed
    Note over Client: Invalidate cache immediately
    Client->>Server: tools/list
    Server-->>Client: { tools: [...], ttlMs: 300000 }
```

## 与分页的交互

当列表结果被[分页](/specification/draft/server/utilities/pagination)时，每一页都是可独立缓存的响应，这与 HTTP `Cache-Control` 处理分页资源的方式一致。

* 每个页面响应都携带自己的 `ttlMs` 值。每页的新鲜度时钟从收到该页的时间开始。
* 服务器 **MAY** 在不同页面上返回不同的 `ttlMs` 值（例如，对稳定列表的早期页面使用较长 TTL，对最后一页使用较短 TTL）。
* 当缓存页过期时，客户端 **SHOULD** 使用其游标重新获取该页。
* 不保证跨页面一致性。如果底层数据在页面获取之间发生变化，客户端可能会观察到重复或缺口。
* 需要完整列表一致快照的客户端 **SHOULD** 从头开始重新获取（不带游标）。
* 如果游标变为无效（例如，服务器对先前有效的游标返回错误），客户端 **SHOULD** 丢弃所有缓存页并从头开始重新获取。

对于给定列表请求，服务器 **MUST** 对所有响应页面应用相同的 `cacheScope`。例如，如果 `tools/list` 响应的第一页具有 `cacheScope: "private"`，则该请求的所有后续页面也 **MUST** 是 `"private"`。

## 安全注意事项

`cacheScope` 为 `"public"` 表示响应不包含用户特定数据，可以安全共享。服务器 MUST 意识到，即使结果来自已认证端点，具有 `"public"` `cacheScope` 的响应也可能在调用方之间共享。例如，带有 `"public"` `cacheScope` 的已认证 `tools/list` 调用结果可能被客户端缓存，并可能在初始请求授权上下文之外共享。（即，不同访问令牌可以利用同一缓存。）

服务器实现者：

* 应确保 `cacheScope` 正确反映原语的预期可见性。
* MUST 应用适当的逐原语访问控制，且 MUST NOT 仅依赖 `cacheScope` 来防止对原语的未授权访问。
