Skip to content
MCP 文档版本 2026-07-28 更新于 2026-10-14

缓存

Model Context Protocol (MCP) 支持对部分结果进行缓存。这使客户端能够缓存响应并减少不必要的重复获取。 缓存与变更通知互为补充——两种机制可以共存。

可缓存结果

服务端 MUST(必须)为以下操作返回的、带有 resultType: "complete" 的结果包含缓存提示:

  • server/discover
  • tools/list
  • prompts/list
  • resources/list
  • resources/templates/list
  • resources/read

带有 resultType: "input_required" 的中间结果(参见 多轮往返请求)不可缓存, 且不携带任何缓存提示。

缓存键

缓存响应由请求方法以及影响结果的请求参数共同标识(例如,resources/read 的 uri,或分页列表请求的 cursor)。对于方法或参数与产生该缓存的请求不同的请求,客户端 MUST NOT(不得)返回缓存响应。

通过多轮往返请求机制重试请求所产生的结果——即携带 inputResponses 或 requestState 的请求——MUST NOT(不得)被缓存, 因为它们依赖于不属于缓存键的输入。

可缓存模型

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

  • 缓存存活时间 (TTL) 字段,ttlMs,是一个以毫秒为单位的整数值,指定客户端 MAY(可以)将结果视为新鲜的时间长度。
  • 缓存范围字段,cacheScope,指示缓存响应的预期范围,取值为 "public" 或 "private"。

缓存存活时间 (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 告诉客户端它可以合理避免重新获取的时长,而不是数据保证 保持不变的时长。

新鲜度计算

客户端记录收到响应的本地时间(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" 是合适的。

与通知的交互

TTL 与服务端推送通知互为补充:

  • 服务端 MAY(可以)提供 ttlMs 而不在其能力中声明 listChanged: true。 在这种情况下,客户端完全依赖基于 TTL 的新鲜度。
  • 服务端 MAY(可以)同时声明 listChanged: true 并提供 ttlMs。在这种情况下, 客户端可以使用 TTL 避免通知之间的不必要重新获取,而通知则充当立即失效信号。

当缓存响应仍然新鲜时收到相关通知,该通知会使缓存响应失效,并且该响应应立即被视为已过期。

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 }

与分页的交互

当列表结果被分页时,每一页都是一个可独立缓存的响应——这与 HTTP Cache-Control 对待分页资源的方式一致。

  • 每个页面响应携带各自的 ttlMs 值。每个页面的新鲜度计时从该页面被接收时开始。
  • 服务端 MAY(可以)在不同页面上返回不同的 ttlMs 值(例如,为稳定列表的靠前页面设置较长的 TTL,为最后一页设置较短的 TTL)。
  • 当某个缓存页面过期时,客户端 SHOULD(应该)使用其 cursor 重新获取该页面。
  • 不存在跨页面一致性保证。如果底层数据在页面获取之间发生变化,客户端可能会观察到重复或缺失。
  • 需要完整列表一致性快照的客户端 SHOULD(应该)从头重新获取(不带 cursor)。
  • 如果某个 cursor 变为无效(例如,服务端对先前有效的 cursor 返回错误),客户端 SHOULD(应该)丢弃所有缓存页面并从头重新获取。

对于给定的列表请求,服务端 MUST(必须)对所有响应页面应用相同的 cacheScope。例如,如果 tools/list 响应的第一页具有 cacheScope: "private",则该请求的所有后续页面 MUST(必须)也为 "private"。

安全注意事项

cacheScope 为 "public" 表示该响应不包含用户特定数据,可以安全共享。服务端 MUST(必须)意识到,即使 Result 来自经过身份验证的端点,具有 "public" cacheScope 的响应也可能在调用方之间共享。例如,来自经过身份验证的 tools/list 调用且具有 "public" cacheScope 的 Result 可能被客户端缓存,并可能在初始请求的授权上下文之外被共享。(即不同的访问令牌可以利用同一缓存)。

服务端实现者:

  • 应确保 cacheScope 正确反映原语的预期可见性。
  • MUST(必须)应用适当的按原语访问控制,且 MUST NOT(不得)仅依赖 cacheScope 来防止对原语的未授权访问。

BytesRouter 是社区翻译站点,所有译文仅供参考,一切以官方英文原文为准。