-
Stefy Lanza (nextime / spora ) authored
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>
2c3e2d8e