Codex never tells MCP servers to stop after tool call interrupt/timeout
Open 💬 2 comments Opened Jun 8, 2026 by a9lim
What version of Codex CLI is running?
codex-cli 0.137.0
What subscription do you have?
Pro 20x
Which model were you using?
gpt-5.5
What platform is your computer?
Darwin 25.6.0 arm64 arm
What terminal emulator and version are you using (if applicable)?
Terminal.app
Codex doctor report
What issue are you seeing?
When using Codex with tools from MCP servers, interrupting or timing out a tool call doesn't notify the server. Codex will drop the request future locally but sends nothing back, so MCP servers will try to execute the tool to completion anyways.
What steps can reproduce the bug?
- Register a stdio MCP server that runs a tool call and logs whether it receives
notifications/cancelled. - Ask Codex to call that tool then interrupt it or let it time out.
- The server never gets
notifications/cancelledand tries to keep running the tool even though its output will never be seen.
What is the expected behavior?
Codex should send notifications/cancelled when tool calls are cancelled so the server can stop executing it.
Additional information
I had Claude fix the bug at a9lim/codex@fix/mcp-client-cancellation
2 Comments
I checked current
mainand this looks like a real lifecycle gap in the MCP tool-call path, not just a UI interruption issue.The cancellation token exists at the generic tool runtime layer, but it does not make it down to the MCP request:
codex-rs/core/src/tools/parallel.rspasses aCancellationTokeninto each tool dispatch and aborts/drops the task when the turn is cancelled, unless the handler opts into waiting for runtime cleanup.codex-rs/core/src/mcp_tool_call.rs::execute_mcp_tool_call()then awaitssess.call_tool(...)directly.codex-rs/core/src/session/mcp.rs::call_tool()forwards tomcp_connection_manager.call_tool(...)without any cancellation token or request-id handle.CancelledNotificationParamhandled as an inbound server-to-client notification incodex-rs/rmcp-client/src/logging_client_handler.rs; I do not see an outbound client-to-server cancellation path tied to a running tool request.So the likely failure mode is: Codex cancels/aborts the local future, but the MCP server still owns a live JSON-RPC request and never receives MCP
notifications/cancelledfor that request id. That matches the reproduction where the server keeps executing after the result has become unobservable to Codex.Patch shape I would expect:
CancellationTokenfires before the MCP response resolves, send MCPnotifications/cancelledto the server with that request id and a reason such as user interrupt/timeout.notifications/cancelled, then assert Codex sends that notification on interrupt/timeout.This is especially important for tools with external side effects: dropping the local future is not equivalent to cancelling the server-side operation.
This also occurs through Code mode: the model can terminate a pending nested MCP call, but the MCP server receives no
notifications/cancelled. The server therefore continues waiting and cannot distinguish the abandoned request from a legitimate long-running call. #29122 contains the Code-mode reproducer.