Allow MCP servers to opt out of tool catalog caching (#33297)

## What changed

- Recognize the experimental `codex/tool-catalog-cache` server capability during MCP initialization.
- When the server advertises `cacheable: false`, clear its shared tool catalog snapshot and prevent subsequent fetches from repopulating the cache.

GitOrigin-RevId: bce98371a68881001d3ab1378fa1548575f2dadc
This commit is contained in:
jif
2026-07-15 09:20:44 +00:00
committed by copyberry
parent 2e1607ee2f
commit 3307ea8b63
2 changed files with 25 additions and 1 deletions
+17
View File
@@ -79,6 +79,10 @@ use tracing::warn;
/// MCP server capability indicating that Codex should include [`SandboxState`]
/// in tool-call request `_meta` under this key.
pub const MCP_SANDBOX_STATE_META_CAPABILITY: &str = "codex/sandbox-state-meta";
/// Experimental MCP server capability for development and testing only; production servers should
/// not use it. Its `cacheable: false` property disables sharing tool definitions across connections.
const MCP_TOOL_CATALOG_CACHE_CAPABILITY: &str = "codex/tool-catalog-cache";
const MCP_TOOL_CATALOG_CACHEABLE_PROPERTY: &str = "cacheable";
pub const OPENAI_FORM_CAPABILITY: &str = "openai/form";
pub(crate) const MCP_TOOLS_LIST_DURATION_METRIC: &str = "codex.mcp.tools.list.duration_ms";
@@ -887,6 +891,19 @@ async fn start_server_task(
.await
.map_err(StartupOutcomeError::from)?;
let server_disables_tool_catalog_cache = initialize_result
.capabilities
.experimental
.as_ref()
.and_then(|experimental| experimental.get(MCP_TOOL_CATALOG_CACHE_CAPABILITY))
.and_then(|capability| capability.get(MCP_TOOL_CATALOG_CACHEABLE_PROPERTY))
.and_then(serde_json::Value::as_bool)
== Some(false);
if server_disables_tool_catalog_cache
&& let Some(cache_context) = tool_catalog_cache_context.as_ref()
{
cache_context.disable();
}
let server_supports_sandbox_state_meta_capability = initialize_result
.capabilities
.experimental
+8 -1
View File
@@ -52,6 +52,7 @@ struct ToolCatalogCacheEntry {
struct ToolCatalogCacheState {
snapshot: Option<ToolCatalogSnapshot>,
last_accepted_generation: u64,
disabled_by_server: bool,
}
struct ToolCatalogSnapshot {
@@ -125,9 +126,15 @@ impl McpToolCatalogCacheContext {
}
}
pub(crate) fn disable(&self) {
let mut state = lock_unpoisoned(&self.entry.state);
state.disabled_by_server = true;
state.snapshot = None;
}
pub(crate) fn publish_if_newest(&self, ticket: McpToolCatalogFetchTicket, tools: &[ToolInfo]) {
let mut state = lock_unpoisoned(&self.entry.state);
if ticket.generation <= state.last_accepted_generation {
if state.disabled_by_server || ticket.generation <= state.last_accepted_generation {
return;
}