Fix Claude CLI mode: transport, real tool calling via MCP shim, dashboard toggle; bump to 0.99.85
CLI mode never worked. Four independent defects, any one fatal:
- The stdin frame used {"type":"user_message",...}; the CLI expects the
Anthropic envelope {"type":"user","message":{...}} and silently discards
anything else, so requests produced no output at all. With
--input-format stream-json the CLI also ignores a prompt passed as an argv
positional, so it must go over stdin.
- _handle_cli_streaming_request() took no tools parameter but was called with
tools=..., raising TypeError on every streaming request.
- The event parser dispatched on a top-level content_block_delta/message_stop,
but the CLI wraps Anthropic events as {"type":"stream_event","event":{...}},
so those branches were dead code.
- Tool definitions were passed to --tools, which only selects built-in tools by
name; a JSON blob there registers nothing.
Tools now reach the model through an MCP stdio shim (claude_mcp_shim.py), which
advertises the caller's definitions via tools/list. It never executes: in the
OpenAI protocol the client runs tools, so the first tool_use ends the turn and
is returned as tool_calls. Past calls/results are replayed as text since each
request is a fresh session, with a system-prompt directive so the model trusts
a replayed result instead of re-calling.
The shim forces one deviation from the intended flag set: --disallowedTools
'mcp__*' is a blanket deny that also blocks the shim, and deny beats
--allowedTools, so it cannot be kept alongside tool calling. It is retained
when no tools are requested; with tools, --strict-mcp-config preserves the same
isolation by loading only our config and ignoring the host's MCP servers.
Consequences of the event model (one assistant event per content block, not per
message): break on message_delta stop_reason == 'tool_use' rather than the first
tool_use, or parallel calls are dropped; dedupe text/arguments across the delta
and assistant paths; and emit sequential tool_call indices, since content-block
indices count text/thinking blocks and leave holes that break client-side
accumulation.
System messages now go to --system-prompt instead of being inlined as user text
the model could ignore. Non-streaming reports real token usage instead of zeros.
Also fix the dashboard toggle that made this unreachable: providers.py imported
_claude_cli_mode from startup, binding a copy of False at import time, while
detection only ever wrote app_state['_claude_cli_mode']. The template therefore
always received False and the use_cli_mode checkbox never rendered. Inject the
value through init() like every other route global, and drop the orphaned
startup global.
Co-Authored-By:
Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Showing
This diff is collapsed.
No preview for this file type
Please
register
or
sign in
to comment