feat(claude_code): APIRequest.mcp_config + disable CLI built-ins (2.0.5)#207
Merged
Merged
Conversation
… (2.0.5)
Phase-I foundation for MCP-wrapped tools on claude_code_cli
sessions. Stage 6 with provider claude_code_cli was the lone
outlier in the otherwise provider-symmetric surface: every SDK
client (anthropic / openai / google / vllm) accepts the canonical
APIRequest.tools and passes schemas natively to the LLM. The CLI
client dropped them — the LLM saw only the CLI's built-in palette
(Bash / Read / Write / Glob / ToolSearch / …) and hallucinated
against it whenever the host's intent referenced a Geny custom
tool. Symptom: "Tool execution complete: 32 calls, 29 errors" on
prod VTuber sessions.
This PR ships the executor-side wire so a companion Geny PR can
synthesize a per-session MCP config that bridges the host's tool
registry to the CLI.
### Added
- APIRequest.mcp_config: Optional[Dict[str, Any]] — per-request
MCP server configuration.
### Changed
- claude_code_argv reads request.mcp_config with precedence over
the per-client kwarg. When ANY MCP config is supplied, the argv
builder also emits:
* --tools "" — disable CLI's built-in tool palette
so the LLM cannot hallucinate against
tools the host has no executor for.
Skipped when caller explicitly passed
allow_tools (mixed surface support).
* --strict-mcp-config — ignore user-level / project-level MCP
config sources so the per-session
bridge is the sole surface.
- Legacy callers without any MCP config keep today's behaviour
exactly (no flags, CLI built-ins available).
### Stage interface preservation
The Stage 6 → Stage 10 → Stage 16 interface is preserved. When
the CLI uses MCP to call a host tool, the call is dispatched
inside the CLI's agentic loop (via the bridge → host HTTP
endpoint) and the final APIResponse carries only the assistant
message — no tool_use blocks for Stage 10 to dispatch. Stage 10
sees no tool_use → naturally no-ops. Stage 16 sees no pending
state → naturally finishes. Memory / persona / persistence stages
run identically because the canonical APIResponse shape is the
same. Anthropic API path keeps the per-iteration tool-dispatch
loop; the CLI path collapses it inside one CLI invocation. Both
produce identical canonical outputs.
### Tests
- test_argv_request_mcp_config_overrides_kwarg
- test_argv_host_mcp_disables_cli_builtins_and_strict
- test_argv_host_mcp_with_explicit_allow_tools_keeps_builtins
- test_argv_no_mcp_no_tools_flag (legacy back-compat)
Full tests/llm_client/ 193/193 pass.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Phase-I foundation for MCP-wrapped tools on claude_code_cli sessions. Surfaces the host's tool registry to the CLI's LLM without breaking the Stage 6 → Stage 10 → Stage 16 pipeline interface.
The problem
Stage 6 with provider
claude_code_cliwas the lone outlier in the otherwise provider-symmetric surface: every SDK client (anthropic / openai / google / vllm) acceptsAPIRequest.toolsand passes schemas natively to the LLM. The CLI client dropped them — the LLM saw only the CLI's built-in palette (Bash/Read/Write/Glob/ToolSearch/ …) and hallucinated against it whenever the host's intent referenced a Geny custom tool. Symptom on prod: "Tool execution complete: 32 calls, 29 errors".What this PR ships (executor side)
APIRequest.mcp_config: Optional[Dict[str, Any]]— per-request MCP server configuration. CLI-based backends serialize to--mcp-config <json>; SDK-based backends ignore it.claude_code_argvreadsrequest.mcp_config(precedence over the per-client kwarg). When ANY MCP config is supplied, also emits:--tools ""— disable CLI's built-in tool palette so the LLM cannot hallucinate against tools the host has no executor for. Skipped when caller explicitly passedallow_tools(mixed surface support).--strict-mcp-config— ignore user-level / project-level MCP config sources so the per-session bridge is the sole surface.A companion Geny PR will ship the actual MCP bridge (stdio script) + HTTP endpoint that consume this wire.
Stage interface preservation
The Stage 6 → Stage 10 → Stage 16 interface is preserved. When the CLI uses MCP to call a host tool, the call is dispatched inside the CLI's agentic loop (via the bridge → host HTTP endpoint) and the final
APIResponsecarries only the assistant message — notool_useblocks for Stage 10 to dispatch. Stage 10 sees notool_use→ naturally no-ops. Stage 16 sees no pending state → naturally finishes. Memory / persona / persistence stages run identically because the canonicalAPIResponseshape is the same.Anthropic API path keeps the per-iteration tool-dispatch loop; the CLI path collapses it inside one CLI invocation. Both produce identical canonical outputs.
Test plan
tests/llm_client/193/193 passRelease
2.0.5. Migration: none — purely additive. Legacy callers' argv is byte-identical.