MCP tool-list changes are never picked up during a session

Open 💬 2 comments Opened Aug 7, 2026 by mmabrouk
💡 Likely answer: A maintainer (github-actions[bot], contributor) responded on this thread — see the highlighted reply below.

Context

We embed Codex in a product through the app server. Our agents use an MCP server whose tool list changes while a session runs. For example, a user connects a new integration mid-session, and the integration's tools become available. The MCP protocol has a standard notification for this: tools/list_changed.

The issue

Codex receives the notification but does not act on it. The handler (codex-rs/rmcp-client/src/logging_client_handler.rs:86) only writes a log line. The tool list the model sees stays as it was when the server first connected.

Codex does refresh its MCP setup between turns (refresh_mcp_if_dirty in codex-rs/core/src/session/mcp.rs). But that refresh only re-reads a server whose configuration changed. It reuses the existing connection and the cached tool list for a server whose config is unchanged (codex-mcp/src/connection_manager.rs:337-362, plus a 30-minute catalog cache in codex-mcp/src/tool_catalog_cache.rs). A server whose tools changed but whose config did not is exactly the case the refresh skips.

The result: a running session can never see new tools from a connected MCP server, even though the server announces them correctly.

Read from source at fcc4ca5 (main).

Proposed solution, in simple words

Two small steps, both inside Codex:

  1. When the tools/list_changed notification arrives, remember which server sent it.
  2. On the next refresh, fetch that one server's tool list again, even though its config is unchanged, and skip its cache entry.

The existing SendElicitation callback in rmcp-client/src/rmcp_client.rs shows how a notification can reach out of the handler, so the plumbing has a precedent. I have not written a patch, because where the invalidation hook belongs is a design call for maintainers.

Happy to test a fix against a real MCP server with a changing tool list.

View original on GitHub ↗

2 Comments

github-actions[bot] contributor · 21 days ago

Potential duplicates detected. Please review them and close your issue if it is a duplicate.

  • #36297

Powered by Codex Action

Tom-R-Main · 16 days ago

I implemented and tested the native MCP 2026-07-28 path for this on current main. The important distinction from the legacy handler path is that the new spec uses an explicit long-lived subscriptions/listen request after server/discover advertises capabilities.tools.listChanged: true; notifications routed to that subscription do not also reach ClientHandler.

Candidate branch: https://github.com/Tom-R-Main/codex/tree/codex/mcp-tool-list-subscriptions
Feature commit: https://github.com/Tom-R-Main/codex/commit/0b40fd83cc

The patch:

  • establishes subscriptions/listen before the initial tools/list, so a change racing the snapshot is queued rather than lost;
  • refetches the affected server with an uncached tools/list;
  • atomically replaces the exact live catalog and advances Codex's catalog revision, invalidating the next model binding without reconnecting;
  • shares that revision counter across connection-manager replacement so reused live connections keep invalidating the active manager;
  • reconnects ended subscription streams with bounded backoff and cancels the long-lived request during MCP shutdown;
  • leaves legacy mode unchanged.

Regression coverage passes for both layers:

CARGO_PROFILE_TEST_DEBUG=0 cargo test -p codex-rmcp-client --test mcp_2026_tool_list_subscriptions
# 2 passed: advertised modern subscription + legacy no-subscription

CARGO_PROFILE_TEST_DEBUG=0 cargo test -p codex-mcp modern_tool_list_subscription_refreshes_live_binding
# 1 passed: subscription-before-list, exact replacement, revision advance, old binding remains immutable

CARGO_PROFILE_DEV_DEBUG=0 cargo clippy --tests -p codex-rmcp-client -p codex-mcp
# passed

I also grounded the flow against the canonical modelcontextprotocol/modelcontextprotocol 2026-07-28 schema and rmcp 3.0.0 subscription API. The repository currently blocks unsolicited external PR creation, so I cannot open the upstream PR unless a maintainer invites it. Happy to submit it immediately if this direction matches the intended architecture.

This candidate intentionally addresses the negotiated 2026 subscription path. The older pre-2026 ClientHandler::on_tool_list_changed path remains a separate compatibility decision.