Skip to content

fix(codex): add startup_timeout_sec to MCP servers to prevent first-run timeouts#971

Open
Lidang-Jiang wants to merge 4 commits intoaffaan-m:mainfrom
Lidang-Jiang:fix/codex-mcp-startup-timeout
Open

fix(codex): add startup_timeout_sec to MCP servers to prevent first-run timeouts#971
Lidang-Jiang wants to merge 4 commits intoaffaan-m:mainfrom
Lidang-Jiang:fix/codex-mcp-startup-timeout

Conversation

@Lidang-Jiang
Copy link
Copy Markdown
Contributor

@Lidang-Jiang Lidang-Jiang commented Mar 28, 2026

Summary

  • Add startup_timeout_sec = 30 to all five command-based MCP servers in .codex/config.toml
  • Sync startup_timeout_sec into merge-mcp-config.js ECC_SERVERS to prevent --update-mcp drift
  • Align @upstash/context7-mcp@latest package specifier between config.toml and merge script
  • Extract DEFAULT_MCP_STARTUP_TIMEOUT_SEC constant to reduce duplication (CodeRabbit nitpick)
  • Prevents "timed out after 10 seconds" errors on first startup when npx needs to download packages

Problem

When Codex starts for the first time (or after clearing npm cache), MCP servers launched via npx need to download their packages before responding. The default startup timeout is too short, causing errors like:

MCP client for github timed out after 10 seconds
MCP client for sequential-thinking timed out after 10 seconds
MCP client for playwright timed out after 10 seconds

Changes

Server Change
github + startup_timeout_sec = 30
context7 + startup_timeout_sec = 30
memory + startup_timeout_sec = 30
playwright + startup_timeout_sec = 30
sequential-thinking + startup_timeout_sec = 30
exa No change (URL-based, no npx)

30 seconds was chosen over the 20s precedent in merge-mcp-config.js to give extra headroom for slow networks on first run.

Additionally, per CodeRabbit reviewer feedback, the hardcoded 30 values in merge-mcp-config.js have been extracted into shared constants DEFAULT_MCP_STARTUP_TIMEOUT_SEC and DEFAULT_MCP_STARTUP_TIMEOUT_TOML to prevent divergence and simplify future tuning.

Before (TOML syntax validation + test suite — prior to constant extraction)
$ python3 -c "import tomllib; tomllib.load(open('.codex/config.toml','rb')); print('TOML syntax OK')"
TOML syntax OK

$ node tests/run-all.js
╔══════════════════════════════════════════════════════════╗
║           Everything Claude Code - Test Suite            ║
╚══════════════════════════════════════════════════════════╝

━━━ Running codex-config.test.js ━━━
  ✓ reference config does not pin a top-level model
  ✓ reference config does not pin a top-level model provider
  ✓ sample Codex role configs do not use o4-mini
Passed: 3
Failed: 0

... (1621 tests across all suites) ...

╔══════════════════════════════════════════════════════════╗
║                     Final Results                        ║
╠══════════════════════════════════════════════════════════╣
║  Total Tests: 1621                                       ║
║  Passed:      1621  ✓                                    ║
║  Failed:         0                                       ║
╚══════════════════════════════════════════════════════════╝
After (TOML syntax validation + constant verification + test suite — after constant extraction)
$ python3 -c "import tomllib; tomllib.load(open('.codex/config.toml','rb')); print('TOML syntax OK')"
TOML syntax OK

$ node -e "const fs=require('fs'); const src=fs.readFileSync('scripts/codex/merge-mcp-config.js','utf8'); const m=src.match(/DEFAULT_MCP_STARTUP_TIMEOUT_SEC = (\d+)/); console.log('DEFAULT_MCP_STARTUP_TIMEOUT_SEC =', m[1]); console.log('Total usages:', (src.match(/DEFAULT_MCP_STARTUP_TIMEOUT_SEC/g)||[]).length); console.log('TOML usages:', (src.match(/DEFAULT_MCP_STARTUP_TIMEOUT_TOML/g)||[]).length)"
DEFAULT_MCP_STARTUP_TIMEOUT_SEC = 30
Total usages: 7
TOML usages: 6

$ node tests/run-all.js
╔══════════════════════════════════════════════════════════╗
║           Everything Claude Code - Test Suite            ║
╚══════════════════════════════════════════════════════════╝

━━━ Running codex-config.test.js ━━━
  ✓ reference config does not pin a top-level model
  ✓ reference config does not pin a top-level model provider
  ✓ sample Codex role configs do not use o4-mini
Passed: 3
Failed: 0

... (1621 tests across all suites) ...

╔══════════════════════════════════════════════════════════╗
║                     Final Results                        ║
╠══════════════════════════════════════════════════════════╣
║  Total Tests: 1621                                       ║
║  Passed:      1621  ✓                                    ║
║  Failed:         0                                       ║
╚══════════════════════════════════════════════════════════╝

Reviewer feedback addressed

  • CodeRabbit: Synced startup_timeout_sec into merge-mcp-config.js ECC_SERVERS to prevent --update-mcp drift ✅
  • CodeRabbit: Aligned @upstash/context7-mcp@latest package specifier ✅
  • CodeRabbit (nitpick): Extracted DEFAULT_MCP_STARTUP_TIMEOUT_SEC constant ✅
  • greptile: Same merge drift concern ✅

Test plan

  • Verify TOML syntax: python3 -c "import tomllib; tomllib.load(open('.codex/config.toml','rb'))"
  • Verify constant extraction is correct and all servers reference it
  • Full test suite: 1621/1621 passing
  • Confirm Codex starts without MCP timeout errors on a fresh npm cache
  • Verify existing MCP server functionality is unaffected

Fixes #544

…un timeouts

On first startup, npx-based MCP servers need to download packages before
they can respond. The default timeout is too short for this, causing
frequent "timed out after 10 seconds" errors reported in affaan-m#544.

Add startup_timeout_sec = 30 to all five command-based MCP servers
(github, context7, memory, playwright, sequential-thinking). The URL-based
exa server is unaffected and left unchanged.

30 seconds was chosen over the 20s precedent in merge-mcp-config.js to
give extra headroom for slow networks on first run.

Fixes affaan-m#544

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

Signed-off-by: Lidang-Jiang <lidangjiang@gmail.com>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 28, 2026

📝 Walkthrough

Walkthrough

Adds startup_timeout_sec = 30 to five MCP server entries in .codex/config.toml and updates scripts/codex/merge-mcp-config.js so generated/merged MCP server specs include the same timeout. No other functional changes.

Changes

Cohort / File(s) Summary
Codex config
.codex/config.toml
Inserted startup_timeout_sec = 30 into MCP server sections: github, context7, memory, playwright, and sequential-thinking.
Merge script
scripts/codex/merge-mcp-config.js
Added startup_timeout_sec: 30 to drift-detection fields and appended startup_timeout_sec = 30 in the generated TOML blocks for the affected MCP servers (playwright, context7-mcp, memory, sequential-thinking, and github). Left supabase handling unchanged.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related PRs

Suggested reviewers

  • affaan-m

Poem

🐰 I hopped through configs, nudged time to thirty,
Five sleepy servers wake now calm and sturdy.
No frantic beeps at dawn’s first start,
Just gentle spins and peaceful art. 🥕✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: adding startup_timeout_sec to MCP servers to address first-run timeout issues.
Linked Issues check ✅ Passed The PR successfully implements the primary mitigation from issue #544 by increasing startup_timeout_sec to 30 for command-based MCP servers.
Out of Scope Changes check ✅ Passed All changes are directly related to implementing the startup timeout fix for MCP servers as specified in issue #544.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

No issues found across 1 file

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Mar 28, 2026

Greptile Summary

This PR fixes first-run MCP server timeout errors by adding startup_timeout_sec = 30 to the five npx-based MCP servers in .codex/config.toml, and keeps that setting durable by also updating ECC_SERVERS in scripts/codex/merge-mcp-config.js so that --update-mcp preserves the timeout rather than reverting it.

  • .codex/config.toml: startup_timeout_sec = 30 added to github, context7, memory, playwright, and sequential-thinking; exa (URL-based) correctly left unchanged.
  • scripts/codex/merge-mcp-config.js: All five servers in ECC_SERVERS now include the timeout in both the fields object (for drift detection) and the raw toml string (for file generation). The context7-mcp package reference is also updated to @upstash/context7-mcp@latest, aligning it with config.toml.
  • The only minor observation is that startup_timeout_sec uses integer 30 while the pre-existing supabase entry uses float 20.0; for strictest TOML-type compatibility, 30.0 would be more consistent.

Confidence Score: 5/5

Safe to merge — the fix is straightforward and the previously-flagged regression path (merge script reverting the timeout) has been correctly addressed.

No P0 or P1 issues remain. The only finding is a P2 style suggestion about integer vs float literal consistency that is unlikely to cause a real runtime problem. The prior concern about --update-mcp reverting the fix is fully resolved.

No files require special attention.

Important Files Changed

Filename Overview
.codex/config.toml Added startup_timeout_sec = 30 to all five npx-based MCP servers; exa (URL-based) correctly left untouched. TOML is syntactically valid.
scripts/codex/merge-mcp-config.js ECC_SERVERS updated to include startup_timeout_sec: 30 in both fields and toml for playwright, context7-mcp, memory, sequential-thinking, and github, preventing --update-mcp from reverting the fix. Also adds @latest to context7-mcp package tag for consistency with config.toml.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[Codex starts MCP server via npx] --> B{Packages cached?}
    B -- Yes --> C[Server responds quickly]
    B -- No --> D[npx downloads package]
    D --> E{Startup timeout elapsed?}
    E -- Before fix: 10s default --> F[❌ Timeout error]
    E -- After fix: 30s --> G[✅ Server ready]
    C --> G

    subgraph merge-mcp-config.js
        H[--update-mcp flag] --> I[removeServerFromText]
        I --> J[Append ECC_SERVERS spec]
        J --> K[startup_timeout_sec = 30 preserved]
    end
Loading

Reviews (3): Last reviewed commit: "fix(codex): align context7-mcp package s..." | Re-trigger Greptile

Comment on lines 38 to +64
[mcp_servers.github]
command = "npx"
args = ["-y", "@modelcontextprotocol/server-github"]
startup_timeout_sec = 30

[mcp_servers.context7]
command = "npx"
args = ["-y", "@upstash/context7-mcp@latest"]
startup_timeout_sec = 30

[mcp_servers.exa]
url = "https://mcp.exa.ai/mcp"

[mcp_servers.memory]
command = "npx"
args = ["-y", "@modelcontextprotocol/server-memory"]
startup_timeout_sec = 30

[mcp_servers.playwright]
command = "npx"
args = ["-y", "@playwright/mcp@latest", "--extension"]
startup_timeout_sec = 30

[mcp_servers.sequential-thinking]
command = "npx"
args = ["-y", "@modelcontextprotocol/server-sequential-thinking"]
startup_timeout_sec = 30
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.

P1 startup_timeout_sec not added to merge-mcp-config.js — fix will be reverted on next update

The companion script scripts/codex/merge-mcp-config.js maintains its own ECC_SERVERS definitions (lines 86–100) that are used to add or update MCP server configs. None of the dlxServer(...) calls for playwright, context7-mcp, memory, sequential-thinking, or github include startup_timeout_sec:

playwright: dlxServer('playwright', '@playwright/mcp@latest'),
'context7-mcp': dlxServer('context7-mcp', '@upstash/context7-mcp'),
memory: dlxServer('memory', '@modelcontextprotocol/server-memory'),
'sequential-thinking': dlxServer('sequential-thinking', ...),
github: { fields: { command: 'bash', ... }, toml: ... }

This creates two problems:

  1. --update-mcp reverts the fix: When users run node merge-mcp-config.js <config.toml> --update-mcp, removeServerFromText strips each section and re-appends it from ECC_SERVERS, which has no startup_timeout_sec. The timeout fix is silently lost.

  2. Add-only mode emits drift warnings: configDiffers() compares the user's existing config against spec.fields. Because startup_timeout_sec is now in config.toml but not in spec.fields, every affected server will trigger a WARNING: mcp_servers.<name> differs from ECC recommendation message, even though the user's config is intentionally better.

merge-mcp-config.js should be updated to include startup_timeout_sec in each relevant server's extraFields and extraToml, similar to how supabase already does it (line 87):

startup_timeout_sec: 30,

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Addressed in commit 1e44475 — synced startup_timeout_sec into merge-mcp-config.js ECC_SERVERS for all five command-based servers. Additionally, extracted a shared constant DEFAULT_MCP_STARTUP_TIMEOUT_SEC in commit 78c98dd to prevent future divergence.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

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 the current code and only fix it if needed.

Inline comments:
In @.codex/config.toml:
- Around line 41-64: The new startup_timeout_sec = 30 entries were added to the
MCP server blocks but the generator/spec (ECC_SERVERS in
scripts/codex/merge-mcp-config.js) wasn’t updated, which will cause merge drift;
update the ECC_SERVERS definition in scripts/codex/merge-mcp-config.js to
include startup_timeout_sec: 30 for the matching server entries (context7,
memory, playwright, sequential-thinking, etc.) so the generated merge spec
matches .codex/config.toml and prevents --update-mcp from removing these values.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7d1c4782-19cc-44e8-9701-cf936ff04c70

📥 Commits

Reviewing files that changed from the base of the PR and between 8b6140d and 00f8628.

📒 Files selected for processing (1)
  • .codex/config.toml

…VERS

Reviewers identified that merge-mcp-config.js --update-mcp would
silently strip the startup_timeout_sec from config.toml because the
ECC_SERVERS spec did not include it. Add startup_timeout_sec = 30 to
playwright, context7-mcp, github, memory, and sequential-thinking.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

Signed-off-by: Lidang-Jiang <lidangjiang@gmail.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

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)
scripts/codex/merge-mcp-config.js (1)

88-99: Extract repeated startup_timeout_sec = 30 into a shared constant.

This block duplicates the same timeout literal in multiple places. Centralizing it prevents accidental divergence and makes future tuning one-line.

♻️ Proposed refactor
+const DEFAULT_MCP_STARTUP_TIMEOUT_SEC = 30;
+const DEFAULT_MCP_STARTUP_TIMEOUT_TOML = `startup_timeout_sec = ${DEFAULT_MCP_STARTUP_TIMEOUT_SEC}`;
+
 const ECC_SERVERS = {
   supabase: dlxServer('supabase', '@supabase/mcp-server-supabase@latest', { startup_timeout_sec: 20.0, tool_timeout_sec: 120.0 }, 'startup_timeout_sec = 20.0\ntool_timeout_sec = 120.0'),
-  playwright: dlxServer('playwright', '@playwright/mcp@latest', { startup_timeout_sec: 30 }, 'startup_timeout_sec = 30'),
-  'context7-mcp': dlxServer('context7-mcp', '@upstash/context7-mcp', { startup_timeout_sec: 30 }, 'startup_timeout_sec = 30'),
+  playwright: dlxServer('playwright', '@playwright/mcp@latest', { startup_timeout_sec: DEFAULT_MCP_STARTUP_TIMEOUT_SEC }, DEFAULT_MCP_STARTUP_TIMEOUT_TOML),
+  'context7-mcp': dlxServer('context7-mcp', '@upstash/context7-mcp@latest', { startup_timeout_sec: DEFAULT_MCP_STARTUP_TIMEOUT_SEC }, DEFAULT_MCP_STARTUP_TIMEOUT_TOML),
   exa: {
     fields: { url: 'https://mcp.exa.ai/mcp' },
     toml: `[mcp_servers.exa]\nurl = "https://mcp.exa.ai/mcp"`
   },
   github: {
-    fields: { command: 'bash', args: ['-lc', GH_BOOTSTRAP], startup_timeout_sec: 30 },
-    toml: `[mcp_servers.github]\ncommand = "bash"\nargs = ["-lc", ${JSON.stringify(GH_BOOTSTRAP)}]\nstartup_timeout_sec = 30`
+    fields: { command: 'bash', args: ['-lc', GH_BOOTSTRAP], startup_timeout_sec: DEFAULT_MCP_STARTUP_TIMEOUT_SEC },
+    toml: `[mcp_servers.github]\ncommand = "bash"\nargs = ["-lc", ${JSON.stringify(GH_BOOTSTRAP)}]\n${DEFAULT_MCP_STARTUP_TIMEOUT_TOML}`
   },
-  memory: dlxServer('memory', '@modelcontextprotocol/server-memory', { startup_timeout_sec: 30 }, 'startup_timeout_sec = 30'),
-  'sequential-thinking': dlxServer('sequential-thinking', '@modelcontextprotocol/server-sequential-thinking', { startup_timeout_sec: 30 }, 'startup_timeout_sec = 30')
+  memory: dlxServer('memory', '@modelcontextprotocol/server-memory', { startup_timeout_sec: DEFAULT_MCP_STARTUP_TIMEOUT_SEC }, DEFAULT_MCP_STARTUP_TIMEOUT_TOML),
+  'sequential-thinking': dlxServer('sequential-thinking', '@modelcontextprotocol/server-sequential-thinking', { startup_timeout_sec: DEFAULT_MCP_STARTUP_TIMEOUT_SEC }, DEFAULT_MCP_STARTUP_TIMEOUT_TOML)
 };

As per coding guidelines: "Do not use hardcoded values; use constants or configuration instead."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/codex/merge-mcp-config.js` around lines 88 - 99, Introduce a single
constant (e.g., STARTUP_TIMEOUT_SEC = 30) and replace all hardcoded 30
occurrences used for startup timeouts: update object literals like the second
argument to dlxServer calls (currently { startup_timeout_sec: 30 }) and replace
the repeated string fragments ('startup_timeout_sec = 30') used in toml/string
args with a template built from that constant; ensure places referenced in the
diff—keys for 'playwright', 'context7-mcp', 'github', 'memory',
'sequential-thinking', and any dlxServer(...) invocations—use the new
STARTUP_TIMEOUT_SEC variable so both the JS objects and the toml strings remain
consistent.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@scripts/codex/merge-mcp-config.js`:
- Line 89: The package specifier for the 'context7-mcp' entry is inconsistent
and causes configDiffers to flag drift; update the dlxServer call for
'context7-mcp' (the line invoking dlxServer('context7-mcp',
'@upstash/context7-mcp', ...)) to use the exact same package string
'@upstash/context7-mcp@latest' as in .codex/config.toml so configDiffers
performs an exact match; ensure the change is applied where dlxServer is called
so future comparisons won't trigger needless rewrites.

---

Nitpick comments:
In `@scripts/codex/merge-mcp-config.js`:
- Around line 88-99: Introduce a single constant (e.g., STARTUP_TIMEOUT_SEC =
30) and replace all hardcoded 30 occurrences used for startup timeouts: update
object literals like the second argument to dlxServer calls (currently {
startup_timeout_sec: 30 }) and replace the repeated string fragments
('startup_timeout_sec = 30') used in toml/string args with a template built from
that constant; ensure places referenced in the diff—keys for 'playwright',
'context7-mcp', 'github', 'memory', 'sequential-thinking', and any
dlxServer(...) invocations—use the new STARTUP_TIMEOUT_SEC variable so both the
JS objects and the toml strings remain consistent.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 8ac7c3d6-85e6-40a0-8ce4-ff1d77429277

📥 Commits

Reviewing files that changed from the base of the PR and between 00f8628 and 1e44475.

📒 Files selected for processing (1)
  • scripts/codex/merge-mcp-config.js

Lidang-Jiang and others added 2 commits March 28, 2026 11:38
Add @latest suffix to '@upstash/context7-mcp' in ECC_SERVERS so the
generated merge spec matches .codex/config.toml exactly, preventing
configDiffers from flagging false drift on --update-mcp runs.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

Signed-off-by: Lidang-Jiang <lidangjiang@gmail.com>
Copy link
Copy Markdown
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.

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.

MCP servers in default Codex config frequently timeout on first startup

2 participants