feat(claude): warm up sessions for agents that expose MCPs
`ClaudeCodeOptions.warmup_turn` defaults to `None`, which decides per agent: on when the agent exposes any MCP, off otherwise. An agent whose tools are the point shouldn't answer its first message without them, and an agent with no MCPs shouldn't pay for a warm-up it gains nothing from. The underlying reason lives in claude-code-api: the CLI connects MCP servers when a turn starts and doesn't await them, so turn one runs with built-in tools only.
This commit is contained in:
@@ -111,6 +111,25 @@ class ClaudeCodeOptions(BaseModel):
|
|||||||
would take claude out of print mode. Everything else — model,
|
would take claude out of print mode. Everything else — model,
|
||||||
prompts, tools, MCPs, history seeding — behaves identically."""
|
prompts, tools, MCPs, history seeding — behaves identically."""
|
||||||
|
|
||||||
|
warmup_turn: bool | None = None
|
||||||
|
"""Spend a throwaway turn when a session spawns, so MCP tools are
|
||||||
|
live on the agent's first reply.
|
||||||
|
|
||||||
|
``None`` (the default) decides per agent: on when it exposes any
|
||||||
|
MCP, off when it doesn't. Set it explicitly to override.
|
||||||
|
|
||||||
|
It exists because the CLI begins connecting MCP servers when a turn
|
||||||
|
starts and doesn't wait for them — measured on the pi, turn one saw
|
||||||
|
29 tools with both servers ``pending``, turn two saw 90 with both
|
||||||
|
``connected``. For an agent whose whole job is those tools, a first
|
||||||
|
reply without them is worse than a couple of extra seconds. Nothing
|
||||||
|
cheaper works: sleeping longer changes nothing, and neither a
|
||||||
|
``/status`` command nor the SDK's ``initialize`` handshake touches
|
||||||
|
the MCP client.
|
||||||
|
|
||||||
|
``stream_json`` only — the PTY transport's readiness wait covers
|
||||||
|
this incidentally."""
|
||||||
|
|
||||||
include_partial_messages: bool = False
|
include_partial_messages: bool = False
|
||||||
"""Stream token-level deltas to the client instead of one delta per
|
"""Stream token-level deltas to the client instead of one delta per
|
||||||
finished block. Requires ``transport="stream_json"``; inert on the
|
finished block. Requires ``transport="stream_json"``; inert on the
|
||||||
|
|||||||
@@ -197,6 +197,12 @@ def _build_backend_options(
|
|||||||
mcp_servers=_build_mcp_servers(agent, mcp_internal_urls),
|
mcp_servers=_build_mcp_servers(agent, mcp_internal_urls),
|
||||||
transport=opt.transport,
|
transport=opt.transport,
|
||||||
include_partial_messages=opt.include_partial_messages,
|
include_partial_messages=opt.include_partial_messages,
|
||||||
|
# ``None`` means "decide from the agent": an agent that exposes
|
||||||
|
# MCPs needs them on its first reply, one that doesn't shouldn't
|
||||||
|
# pay for a warm-up it gains nothing from.
|
||||||
|
warmup_turn=(
|
||||||
|
bool(agent.expose_mcps) if opt.warmup_turn is None else opt.warmup_turn
|
||||||
|
),
|
||||||
disallowed_tools=opt.disallowed_tools,
|
disallowed_tools=opt.disallowed_tools,
|
||||||
permission_mode=opt.permission_mode,
|
permission_mode=opt.permission_mode,
|
||||||
dangerously_skip_permissions=opt.dangerously_skip_permissions,
|
dangerously_skip_permissions=opt.dangerously_skip_permissions,
|
||||||
|
|||||||
@@ -287,7 +287,7 @@ local = [
|
|||||||
{ name = "raycast-api", version = "0.1.0", source = { editable = "../raycast-api" } },
|
{ name = "raycast-api", version = "0.1.0", source = { editable = "../raycast-api" } },
|
||||||
]
|
]
|
||||||
prod = [
|
prod = [
|
||||||
{ name = "claude-code-api", version = "0.1.0", source = { git = "https://git.kotikot.com/beaver/claude-code-api.git#b50911c3edb62e30597b6b96739b888c8be853fe" } },
|
{ name = "claude-code-api", version = "0.1.0", source = { git = "https://git.kotikot.com/beaver/claude-code-api.git#621da90623279949fbe47b80bfa57a7c8c6bf74b" } },
|
||||||
{ name = "raycast-api", version = "0.1.0", source = { git = "https://git.kotikot.com/beaver/raycast-api.git#e73894c8e435da5c0709f92f69f11bcd0dab9afe" } },
|
{ name = "raycast-api", version = "0.1.0", source = { git = "https://git.kotikot.com/beaver/raycast-api.git#e73894c8e435da5c0709f92f69f11bcd0dab9afe" } },
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -419,7 +419,7 @@ wheels = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "claude-code-api"
|
name = "claude-code-api"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = { git = "https://git.kotikot.com/beaver/claude-code-api.git#b50911c3edb62e30597b6b96739b888c8be853fe" }
|
source = { git = "https://git.kotikot.com/beaver/claude-code-api.git#621da90623279949fbe47b80bfa57a7c8c6bf74b" }
|
||||||
resolution-markers = [
|
resolution-markers = [
|
||||||
"python_full_version >= '3.14'",
|
"python_full_version >= '3.14'",
|
||||||
"python_full_version < '3.14'",
|
"python_full_version < '3.14'",
|
||||||
|
|||||||
Reference in New Issue
Block a user