Skip to content

DEV-1478: generous SLayer MCP startup timeout - #7

Merged
ZmeiGorynych merged 6 commits into
mainfrom
egor/dev-1478-mcp-startup-timeout
May 29, 2026
Merged

DEV-1478: generous SLayer MCP startup timeout#7
ZmeiGorynych merged 6 commits into
mainfrom
egor/dev-1478-mcp-startup-timeout

Conversation

@ZmeiGorynych

@ZmeiGorynych ZmeiGorynych commented May 28, 2026

Copy link
Copy Markdown
Member

Summary

  • The slayer stdio MCP server runs --ingest-on-startup, whose cost is a datasource schema re-reflection + semantic-layer rebuild (~30–50s for a large schema like alien, 30+ models). Under multi-actor CPU contention this blew the prior 300s MCP initialize() handshake budget, so every large-schema task timed out before any LLM call (DEV-1478 cloud run: all 10 alien, both cold_chain, exchange_traded_funds, and 4 credit tasks died at the handshake with TimeoutError).
  • Embeddings are not the cost — they're prebuilt in the reference's embeddings.db, copied into each task variant, and hash-skipped on startup (verified: re-run leaves embeddings.db untouched).
  • Fix: a shared, documented SLAYER_MCP_STARTUP_TIMEOUT_S = 1800 in harness.py, wired into all three MCPServerStdio sites (pydantic_ai, pydantic_ai_otf_encode, pydantic_ai_recursive). ~30–50x margin over the uncontended cost so contention can't trip it; a truly hung handshake is still bounded by the run's max-runtime / no-progress deadline.

Test plan

  • tests/test_mcp_startup_timeout.py — asserts the budget is generous (≥1800) and that each adapter's MCP server is constructed with the shared value.
  • Full non-integration suite green (1248 passed).
  • Re-run the 17 timed-out tasks on fatter workers (e2-standard-8, 2 actors/worker) to confirm no MCP-startup timeouts.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • Increased MCP server startup timeout to 1800 seconds to improve reliability during heavy initialization.
  • New Features

    • Reference builds now record and persist per-build setup-encoder token usage.
    • The setup-encoder runner exposes accumulated usage data for downstream persistence and partial-marking on capture failure.
  • Tests

    • Added tests validating the shared startup timeout and ensuring setup-encoder usage is captured, marked on failures, and persisted.

Review Change Stack

The slayer stdio MCP server runs `--ingest-on-startup`, whose cost is a
datasource schema RE-REFLECTION + semantic-layer rebuild (~30-50s for a
large schema like alien). Under multi-actor CPU contention that blew the
prior 300s MCP `initialize()` handshake budget, so every large-schema
task timed out before any LLM call. Embeddings are NOT the cost — they're
prebuilt and hash-skipped on startup.

Add a shared, documented `SLAYER_MCP_STARTUP_TIMEOUT_S = 1800` in
harness.py and use it at all three MCPServerStdio sites (pydantic_ai,
otf_encode, recursive); ~30-50x margin so contention can't trip it.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@linear

linear Bot commented May 28, 2026

Copy link
Copy Markdown

DEV-1478

@coderabbitai

coderabbitai Bot commented May 28, 2026

Copy link
Copy Markdown
Contributor

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 01afc45e-598f-4dd7-8ad6-938ab735ff3f

📥 Commits

Reviewing files that changed from the base of the PR and between 6f734fe and 7fc73fa.

📒 Files selected for processing (1)
  • tests/test_mcp_startup_timeout.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/test_mcp_startup_timeout.py

📝 Walkthrough

Walkthrough

Adds SLAYER_MCP_STARTUP_TIMEOUT_S = 1800 in harness.py and uses it for MCPServerStdio startup timeouts in agent modules; threads a shared TokenUsage accumulator through setup-encoder runs, exposes it on run_one, persists per-DB _setup_usage.json during reference build finalization; tests updated/added.

Changes

MCP Startup Timeout Consolidation

Layer / File(s) Summary
Timeout constant definition
src/bird_interact_agents/harness.py
Introduces SLAYER_MCP_STARTUP_TIMEOUT_S = 1800 with inline rationale describing the startup-handshake timeout budget.
Apply constant across agent implementations
src/bird_interact_agents/agents/pydantic_ai/agent.py, src/bird_interact_agents/agents/pydantic_ai_otf_encode/agent.py, src/bird_interact_agents/agents/pydantic_ai_recursive/agent.py
Each agent imports the constant and replaces hardcoded timeout=300 with timeout=SLAYER_MCP_STARTUP_TIMEOUT_S for MCPServerStdio.
Test timeout constant usage
tests/test_mcp_startup_timeout.py
New tests assert the shared timeout is at least 1800s and that all adapter/server construction paths use the constant.

Setup-encoder usage instrumentation & persistence

Layer / File(s) Summary
Instrument setup_encoder with TokenUsage
src/bird_interact_agents/agents/pydantic_ai_otf_encode/setup_encoder.py
Import TokenUsage; thread an optional `usage: TokenUsage
Persist per-DB setup usage in reference build
src/bird_interact_agents/slayer_otf/reference_build.py
Introduce _SETUP_USAGE and write _setup_usage.json from run_one.usage during build-finalization before writing the reference fingerprint marker.
Tests updated for usage plumbing
tests/test_pydantic_ai_otf_encode_setup_encoder.py, tests/test_slayer_otf_reference_build.py, tests/test_usage.py
Update test helper signatures to accept usage=None; add tests ensuring _setup_usage.json is persisted and that setup_encoder calls are recorded in TokenUsage.breakdown without rolling into per-task subtotals.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • MotleyAI/bird-agents#4: Overlapping changes to the setup-encoder prompt/rendering plumbing and related encoder pipeline edits.

Poem

🐰 I hopped through code to change a scattered timeout,

Now eighteen hundred seconds guard the startup route.
I tallied encoder tokens, kept per-build score,
Persisted usage files, and tests check the door.
🥕✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 43.24% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the main change: introducing a generous MCP startup timeout constant for the SLayer MCP server to prevent timeouts.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch egor/dev-1478-mcp-startup-timeout

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/bird_interact_agents/agents/pydantic_ai_recursive/agent.py (1)

10-10: 💤 Low value

Consider updating the stale timeout reference.

The comment mentions "Slayer MCP startup is up to 300s" but the timeout is now 1800s. While the comment's main point (explaining why one server is shared per task) remains valid, the specific timing reference is outdated.

📝 Suggested update
-* One ``MCPServerStdio`` per task, shared across root → every sub-agent
-  → query-constructor. Slayer MCP startup is up to 300s with
-  ``--ingest-on-startup``; per-agent spawn would dominate wall time.
+* One ``MCPServerStdio`` per task, shared across root → every sub-agent
+  → query-constructor. Slayer MCP startup can take significant time with
+  ``--ingest-on-startup`` (especially under CPU contention); per-agent 
+  spawn would dominate wall time.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/bird_interact_agents/agents/pydantic_ai_recursive/agent.py` at line 10,
Update the stale timeout reference in the comment that mentions "Slayer MCP
startup is up to 300s" (near the "query-constructor" comment) to the current
1800s value and adjust the wording to reflect the longer startup window while
preserving the explanation for sharing one server per task; search for the
"query-constructor" marker or the "Slayer MCP" text in
src/bird_interact_agents/agents/pydantic_ai_recursive/agent.py and replace
"300s" with "1800s" (or rephrase to "up to 1800s / 30 minutes") so the comment
is accurate.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/test_mcp_startup_timeout.py`:
- Around line 26-41: Add a test that verifies the pydantic_ai adapter uses the
shared startup timeout by calling _build_slayer_agent from
bird_interact_agents.agents.pydantic_ai.agent with test arguments (e.g., model,
slayer_storage_dir using tmp_path, strict_value=False), then inspect the
returned agent's toolsets (agent._toolsets) to locate the constructed slayer
server (first toolset) and assert its timeout equals
SLAYER_MCP_STARTUP_TIMEOUT_S; ensure the test mirrors existing tests' style and
imports and includes an assertion that agent._toolsets is non-empty before
checking slayer_server.timeout.

---

Nitpick comments:
In `@src/bird_interact_agents/agents/pydantic_ai_recursive/agent.py`:
- Line 10: Update the stale timeout reference in the comment that mentions
"Slayer MCP startup is up to 300s" (near the "query-constructor" comment) to the
current 1800s value and adjust the wording to reflect the longer startup window
while preserving the explanation for sharing one server per task; search for the
"query-constructor" marker or the "Slayer MCP" text in
src/bird_interact_agents/agents/pydantic_ai_recursive/agent.py and replace
"300s" with "1800s" (or rephrase to "up to 1800s / 30 minutes") so the comment
is accurate.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: c7cb93e8-8d4f-4925-bf13-90537f2a96ff

📥 Commits

Reviewing files that changed from the base of the PR and between faae06b and a44adaf.

📒 Files selected for processing (5)
  • src/bird_interact_agents/agents/pydantic_ai/agent.py
  • src/bird_interact_agents/agents/pydantic_ai_otf_encode/agent.py
  • src/bird_interact_agents/agents/pydantic_ai_recursive/agent.py
  • src/bird_interact_agents/harness.py
  • tests/test_mcp_startup_timeout.py

Comment thread tests/test_mcp_startup_timeout.py
ZmeiGorynych and others added 2 commits May 28, 2026 18:40
The one-time per-DB reference-build encode (setup_encoder) was
uninstrumented — its Opus spend appeared in no usage_json or artifact, so
the OTF "incl encoders" cost was unrecoverable. Capture each KB-encode's
usage under scope "setup_encoder" into a per-DB TokenUsage (exposed on
run_one.usage) and persist it to `_setup_usage.json` next to the
reference, carried back by the cloud merge. The scope is isolated from
the per-task agent/user_sim subtotals so it stays separately summable.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/bird_interact_agents/agents/pydantic_ai_otf_encode/setup_encoder.py`:
- Around line 214-215: When the call to agent_run.usage() fails in the except
Exception block (currently logging via logger.debug with kb_id), mark the usage
accumulator as partial so downstream cost consumers can distinguish
unknown/incomplete from zero; specifically, set usage.partial = True in that
except branch (where logger.debug("setup-encode usage capture failed for
kb_id=%s", kb_id) is called) while keeping the existing log and continuation
behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: fd27f041-a0e7-4c34-8ca2-92c2aa048ce0

📥 Commits

Reviewing files that changed from the base of the PR and between a44adaf and 0d21be7.

📒 Files selected for processing (8)
  • src/bird_interact_agents/agents/pydantic_ai_otf_encode/agent.py
  • src/bird_interact_agents/agents/pydantic_ai_otf_encode/setup_encoder.py
  • src/bird_interact_agents/agents/pydantic_ai_recursive/agent.py
  • src/bird_interact_agents/harness.py
  • src/bird_interact_agents/slayer_otf/reference_build.py
  • tests/test_pydantic_ai_otf_encode_setup_encoder.py
  • tests/test_slayer_otf_reference_build.py
  • tests/test_usage.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/bird_interact_agents/agents/pydantic_ai_recursive/agent.py
  • src/bird_interact_agents/agents/pydantic_ai_otf_encode/agent.py

ZmeiGorynych and others added 3 commits May 29, 2026 11:41
- Cover the 3rd MCP-server site: assert the base pydantic_ai adapter's
  MCPServerStdio (built inline in _build_slayer_agent) uses the shared
  SLAYER_MCP_STARTUP_TIMEOUT_S (Codex + CodeRabbit).
- setup_encoder: flag the per-DB usage accumulator partial=True when an
  agent_run.usage() capture fails, so cost consumers distinguish $0 from
  unmeasured (CodeRabbit) + a focused test.
- Reword the stale "up to 300s" comment in the recursive adapter docstring
  (now 1800s; CodeRabbit nitpick).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…t (DEV-1478)

PR #7 review (Codex): the pydantic_ai-adapter timeout assertion read the
derived `agent.toolsets` view; switch to `agent._user_toolsets` (the raw
user-supplied list, repo convention per test_root_clarifier_no_pinning.py)
so it can't break if pydantic-ai changes what `.toolsets` composes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@ZmeiGorynych
ZmeiGorynych merged commit 88e8b6f into main May 29, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant