Resuming a session with a pending deferred tool fails when the tool comes from an SDK MCP server and the resume passes an empty streaming input. Claude Code reports the tool as unavailable. It never re-invokes the tool. Passing a text prompt instead works.
0.2.139 works. 0.2.140 and later fail.
import anyio, dataclasses, tempfile
from claude_agent_sdk import (ClaudeAgentOptions, HookMatcher, ResultMessage,
create_sdk_mcp_server, query, tool)
ALLOW = []
@tool("flux", "Read the flux", {})
async def flux(args): return {"content": [{"type": "text", "text": "41.7 gauss"}]}
async def gate(inp, tool_use_id, ctx):
dec = "allow" if ALLOW else "defer"
return {"hookSpecificOutput": {"hookEventName": "PreToolUse", "permissionDecision": dec}}
async def no_prompt():
return
yield
opts = ClaudeAgentOptions(
model="haiku", cwd=tempfile.mkdtemp(), setting_sources=[],
mcp_servers={"probe": create_sdk_mcp_server("probe", tools=[flux])},
allowed_tools=["mcp__probe__flux"],
hooks={"PreToolUse": [HookMatcher(matcher="mcp__probe__flux", hooks=[gate])]})
async def run(prompt, **kw):
async for m in query(prompt=prompt, options=dataclasses.replace(opts, **kw)):
if isinstance(m, ResultMessage): return m
async def main():
a = await run("Measure the flux.")
print("leg 1:", a.stop_reason)
ALLOW.append(True)
b = await run(no_prompt(), resume=a.session_id)
print("leg 2:", b.stop_reason, "| is_error:", b.is_error)
anyio.run(main)
Leg 1 defers the tool and ends with tool_deferred. Leg 2 resumes the session to re-invoke the deferred call. Output:
0.2.139 leg 2: end_turn is_error: False
0.2.140 leg 2: tool_deferred_unavailable is_error: True
On 0.2.140 the run also prints:
Task exception was never retrieved
future: <Task finished coro=<Query._handle_control_request() ...>
exception=CLIConnectionError('ProcessTransport is not ready for writing')>
File ".../claude_agent_sdk/_internal/query.py", line 580, in _handle_control_request
await self.transport.write(json.dumps(success_response) + "\n")
File ".../transport/subprocess_cli.py", line 1048, in write
raise CLIConnectionError("ProcessTransport is not ready for writing")
Claude Code logs Deferred tool resume: tool '...' is no longer available (MCP server disconnected or tool removed). The failing run makes no API call. duration_api_ms is 0 and token usage is zero.
Other results:
- Replacing
no_prompt() with a text prompt works on every version.
- Deferring a built-in tool such as
Read the same way resumes correctly. Only SDK MCP tools fail.
- The Claude Code version does not matter. On 0.2.139 the resume works against CLI 2.1.232, 2.1.235 and 2.1.237. On 0.2.142 it fails against all three.
I have not confirmed the mechanism. The traceback shows the SDK failing to answer a control request from Claude Code after the input stream ends.
Environment: Claude Code 2.1.237, Python 3.13, Linux.
Resuming a session with a pending deferred tool fails when the tool comes from an SDK MCP server and the resume passes an empty streaming input. Claude Code reports the tool as unavailable. It never re-invokes the tool. Passing a text prompt instead works.
0.2.139 works. 0.2.140 and later fail.
Leg 1 defers the tool and ends with
tool_deferred. Leg 2 resumes the session to re-invoke the deferred call. Output:On 0.2.140 the run also prints:
Claude Code logs
Deferred tool resume: tool '...' is no longer available (MCP server disconnected or tool removed). The failing run makes no API call.duration_api_msis 0 and token usage is zero.Other results:
no_prompt()with a text prompt works on every version.Readthe same way resumes correctly. Only SDK MCP tools fail.I have not confirmed the mechanism. The traceback shows the SDK failing to answer a control request from Claude Code after the input stream ends.
Environment: Claude Code 2.1.237, Python 3.13, Linux.