Skip to content

Track Codex CLI tool refresh after tools/list_changed: client root cause confirmed upstream #791

Description

@asw101

Summary

This follows #259. The question that
issue left open is now settled from source: Wassette sends
notifications/tools/list_changed correctly. Codex CLI receives, deserializes,
and dispatches it, but does not connect the notification to its model-facing
tool catalogue and therefore never sends another tools/list.

This issue tracks the interoperability impact for Wassette until an upstream
Codex release fixes the client and can be verified against Wassette's dynamic
load-component flow. It is not asking Wassette to work around the client bug
in the protocol implementation.

Why track this here

Loading a component at runtime and immediately exposing its tools is a core
Wassette path. The server is conforming, but users experience the feature as
broken when their MCP client ignores the catalogue change. Keeping a focused
compatibility issue here:

  • gives Wassette maintainers and users visibility into the affected client;
  • gives the Codex reports independent signal that a real MCP server and workflow
    hit the bug;
  • records why a server-side workaround would be the wrong layer; and
  • provides a place to verify the first Codex release that claims a fix.

Confirmed client-side root cause

In Codex CLI 0.149.1:

  1. RMCP 3.1.2 parses the message as
    ServerNotification::ToolListChangedNotification.
  2. RMCP invalidates its own cached tools/list response.
  3. RMCP dispatches the notification to Codex's
    LoggingClientHandler::on_tool_list_changed.
  4. That handler only logs MCP server tool list changed and returns.
  5. Nothing informs codex-mcp, whose ManagedClient.tools remains the vector
    captured by the startup tools/list.
  6. The model-facing immutable McpBinding stays cached at the same
    tool_catalog_revision.

RMCP cache invalidation is passive: it permits a fresh response if the client
later asks for tools/list; it does not send that request itself. Codex never
asks, so the loaded component remains absent from the model's tool catalogue.

The notification is therefore not lost by stdio parsing, protocol negotiation,
a subscription receiver, Plugin Runtime event routing, or app-server
indirection.

Cross-client evidence

The same Wassette binary was driven against three clients in the same harness
run:

Client Protocol After tools/list_changed
Copilot CLI server/discover, 2026-07-28 re-listed after about 9 ms
Claude Code 2.1.241 initialize, 2025-11-25 re-listed after about 2 ms
Codex CLI 0.149.1 initialize, 2025-06-18 sent no subsequent tools/list

A fresh run on 2026-08-29 reproduced the Codex result. Its only tools/list
occurred about 21 seconds before Wassette sent the notification. The session
continued afterward, ruling out a closed connection or timing race.

Codex upstream reports

The primary report, which describes the same cache-disconnection root cause, is:

Related open reports show the same behavior through other paths and versions:

The disconnect remained present in Codex 0.150.1 and in openai/codex main at
commit 0ae94fdd49b05ee7faa4d984d06a68492cb32b54 on 2026-08-28.

An unmerged community implementation exists at:

Its focused refresh test passes, but it predates Codex's current immutable
binding/catalogue-revision architecture and is not directly cherry-pickable.
A current fix must also increment tool_catalog_revision so the cached
McpBinding is replaced.

Proposed scope for this follow-up

  • Record that Wassette's notification behavior is correct and no
    server-side protocol workaround is required.
  • Keep the affected Codex versions and upstream reports linked here for
    user visibility and upstream signal.
  • Document the Codex compatibility limitation where users encounter
    dynamic load-component, if that is appropriate for current Wassette
    documentation.
  • Re-run the cross-client harness against the first Codex release that
    claims a fix.
  • Confirm that newly added, removed, and same-name schema-changed tools all
    refresh before closing this compatibility issue.

The desired upstream fix belongs in Codex. The useful Wassette action is to
retain a clear compatibility record and independently verify the repaired
client against the server behavior that exposed it.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions