Summary
The Prophet arb one-shot flow is blocked because the packaged Seren Desktop Playwright stealth MCP server does not respond to the Content-Length-framed initialize request used by the Prophet skill's Python MCP gateway. The same packaged server does respond to Seren Desktop's newline-delimited JSON-RPC initialize path, so the binary is alive but the caller/server stdio framing contract is mismatched.
This reproduced after the earlier #1921/#1922 cold-start fix. The binary starts and logs readiness, but the Prophet gateway still times out waiting for an MCP response.
Observed failure
Invocation context:
- Skill:
prophet-arb-bot
- Local skill root:
/Users/taariqlewis/.config/seren/skills/prophet-arb-bot
- Seren Desktop bundled MCP script:
/Applications/SerenDesktop.app/Contents/Resources/mcp-servers/playwright-stealth/dist/index.js
- Desktop repo audited:
/Users/taariqlewis/Projects/Seren_Projects/seren-desktop
- Prior run id:
04ce06ee91f646898d3117c5ce0e1a0c
The skill setup command succeeded and applied the Prophet schema. The first live-gated run then failed before any opportunity or order work:
{
"status": "blocked",
"reason": "blocked_auth_unexpected:TimeoutError:Timed out waiting for response from playwright-stealth MCP.",
"run_id": "04ce06ee91f646898d3117c5ce0e1a0c",
"opportunities": [],
"orders": [],
"pairs": [],
"summary": {
"execution_mode": "delta_neutral",
"live_mode": true,
"pairs_pre_discover": 1
}
}
No trades were placed. The blocker happened while cold-starting authentication/browser automation.
Direct binary probe
The packaged node_modules are present:
/Applications/SerenDesktop.app/Contents/Resources/mcp-servers/playwright-stealth/node_modules exists
/Applications/SerenDesktop.app/Contents/Resources/mcp-servers/playwright-stealth/node_modules/@modelcontextprotocol/sdk exists
Content-Length-framed probe against the packaged binary timed out after 5s:
{
"status": "timeout",
"stdout": "",
"stderr": "[playwright-stealth] Stdio transport ready; default browser: chrome\n"
}
Newline-delimited JSON-RPC probe against the same binary produced a valid initialize response on stdout:
{
"stdout": "{\"result\":{\"protocolVersion\":\"2024-11-05\",\"capabilities\":{\"tools\":{}},\"serverInfo\":{\"name\":\"playwright-stealth\",\"version\":\"1.0.0\"}},\"jsonrpc\":\"2.0\",\"id\":1}\n",
"stderr": "[playwright-stealth] Stdio transport ready; default browser: chrome\n"
}
Conclusion: this is not simply a missing binary or missing dependency. The binary did not respond to the Prophet gateway's Content-Length framed initialize, while it did respond to Desktop's line-delimited initialize.
Audited code paths
Desktop MCP client
src-tauri/src/mcp.rs
PLAYWRIGHT_MCP_SCRIPT_RELATIVE_PATH = "mcp-servers/playwright-stealth/dist/index.js" resolves the bundled server path.
resolve_playwright_mcp_script_path validates script location and node_modules/@modelcontextprotocol/sdk.
mcp_connect spawns the server with stdin/stdout/stderr piped and applies embedded runtime env sanitation.
send_request writes newline-delimited JSON-RPC via writeln!(process.stdin, "{}", request_json) and reads one stdout line with read_line.
MCP_INITIALIZE_TIMEOUT bounds the initialize handshake at 15s.
- Stderr is drained in the background, but the outer timeout path can still return a generic timeout because the blocking task owns the child while stuck in
read_line.
Playwright stealth MCP server
mcp-servers/playwright-stealth/src/index.ts
- Uses
StdioServerTransport from @modelcontextprotocol/sdk/server/stdio.js.
main() connects stdio first, then logs:
const transport = new StdioServerTransport();
await server.connect(transport);
console.error(`[playwright-stealth] Stdio transport ready; default browser: ${getActiveBrowserType()}`);
The #1921 fix moved browser detection after server.connect, which fixed one startup timing issue, but it did not prove compatibility with the Content-Length framing used by the Prophet gateway.
Cold-start tests
mcp-servers/playwright-stealth/src/__tests__/cold_start.test.ts
- Regression coverage currently verifies lazy browser detection.
- It does not spawn the compiled server and perform an end-to-end
initialize handshake with the Prophet gateway framing.
Bundling path
scripts/prepare-mcp-servers.ts and src-tauri/tauri.conf.json
prepare:mcp-servers builds the TypeScript server and copies dependencies with hoisted node_modules.
- Tauri resources include
mcp-servers/playwright-stealth/.
- The installed app has the expected built script and node_modules.
Settings migration
src/stores/settings.store.ts
- Adds/repairs the Playwright stealth MCP server entry and resolves old relative script paths to the bundled resource path.
- This path is not the primary failure, because the direct packaged binary probe found and started the bundled script.
External caller path that exposes the bug
/Users/taariqlewis/Projects/Seren_Projects/seren-skills/prophet/prophet-arb-bot/scripts/otp_worker/playwright_mcp_gateway.py
- Sends Content-Length-framed MCP requests and waits for a Content-Length-framed response.
- Against the packaged desktop binary, that path times out even though stderr says the server is ready.
Relevant prior commits audited
Root-cause hypothesis
There is a stdio transport/framing mismatch between at least two supported callers:
- Seren Desktop Rust MCP client uses newline-delimited JSON-RPC.
- Prophet arb Python gateway uses Content-Length-framed MCP.
- The packaged
playwright-stealth binary answers the first style and times out on the second.
Because the server logs readiness on stderr and emits no stdout under Content-Length framing, the operator only sees Timed out waiting for response from playwright-stealth MCP, which makes this look like a dead binary even though the binary is running.
Proposed fix
- Decide and document the supported stdio framing contract for bundled MCP servers.
- Align all first-party callers and the bundled
playwright-stealth server to that contract, or add an adapter/dual-framing layer if both callers must remain supported.
- Add an end-to-end contract test that spawns the built
mcp-servers/playwright-stealth/dist/index.js and sends the exact initialize frame used by the Prophet Python gateway.
- Keep the existing newline-delimited Desktop test path, or explicitly migrate
src-tauri/src/mcp.rs if the canonical contract becomes Content-Length framing.
- Improve timeout diagnostics so the blocked envelope includes at least: server command, resolved script path, framing mode attempted, stderr tail, whether stdout produced bytes, and node_modules validation status.
- Add a packaged-app validation step that probes
/Applications/SerenDesktop.app/Contents/Resources/mcp-servers/playwright-stealth/dist/index.js or the equivalent release resource path before declaring a release good.
Acceptance criteria
- The Prophet one-shot auth/browser startup no longer blocks with
Timed out waiting for response from playwright-stealth MCP when using the Seren Desktop packaged binary.
- The packaged binary returns an MCP initialize response within 5s for the framing used by the Prophet gateway, or the gateway is updated to the officially supported framing and covered by a test.
- Desktop MCP initialize behavior remains covered and does not regress.
- A failing initialize produces actionable diagnostics instead of only a generic timeout.
- A regression test fails if the server starts and logs readiness but emits no initialize response to the supported framing.
Duplicate check
Searched GitHub issues for repo:serenorg/seren-desktop playwright-stealth Content-Length initialize timeout in:title,body; no matching open or closed issue was returned at filing time.
Summary
The Prophet arb one-shot flow is blocked because the packaged Seren Desktop Playwright stealth MCP server does not respond to the Content-Length-framed
initializerequest used by the Prophet skill's Python MCP gateway. The same packaged server does respond to Seren Desktop's newline-delimited JSON-RPC initialize path, so the binary is alive but the caller/server stdio framing contract is mismatched.This reproduced after the earlier #1921/#1922 cold-start fix. The binary starts and logs readiness, but the Prophet gateway still times out waiting for an MCP response.
Observed failure
Invocation context:
prophet-arb-bot/Users/taariqlewis/.config/seren/skills/prophet-arb-bot/Applications/SerenDesktop.app/Contents/Resources/mcp-servers/playwright-stealth/dist/index.js/Users/taariqlewis/Projects/Seren_Projects/seren-desktop04ce06ee91f646898d3117c5ce0e1a0cThe skill setup command succeeded and applied the Prophet schema. The first live-gated run then failed before any opportunity or order work:
{ "status": "blocked", "reason": "blocked_auth_unexpected:TimeoutError:Timed out waiting for response from playwright-stealth MCP.", "run_id": "04ce06ee91f646898d3117c5ce0e1a0c", "opportunities": [], "orders": [], "pairs": [], "summary": { "execution_mode": "delta_neutral", "live_mode": true, "pairs_pre_discover": 1 } }No trades were placed. The blocker happened while cold-starting authentication/browser automation.
Direct binary probe
The packaged node_modules are present:
/Applications/SerenDesktop.app/Contents/Resources/mcp-servers/playwright-stealth/node_modulesexists/Applications/SerenDesktop.app/Contents/Resources/mcp-servers/playwright-stealth/node_modules/@modelcontextprotocol/sdkexistsContent-Length-framed probe against the packaged binary timed out after 5s:
{ "status": "timeout", "stdout": "", "stderr": "[playwright-stealth] Stdio transport ready; default browser: chrome\n" }Newline-delimited JSON-RPC probe against the same binary produced a valid initialize response on stdout:
{ "stdout": "{\"result\":{\"protocolVersion\":\"2024-11-05\",\"capabilities\":{\"tools\":{}},\"serverInfo\":{\"name\":\"playwright-stealth\",\"version\":\"1.0.0\"}},\"jsonrpc\":\"2.0\",\"id\":1}\n", "stderr": "[playwright-stealth] Stdio transport ready; default browser: chrome\n" }Conclusion: this is not simply a missing binary or missing dependency. The binary did not respond to the Prophet gateway's Content-Length framed initialize, while it did respond to Desktop's line-delimited initialize.
Audited code paths
Desktop MCP client
src-tauri/src/mcp.rsPLAYWRIGHT_MCP_SCRIPT_RELATIVE_PATH = "mcp-servers/playwright-stealth/dist/index.js"resolves the bundled server path.resolve_playwright_mcp_script_pathvalidates script location andnode_modules/@modelcontextprotocol/sdk.mcp_connectspawns the server with stdin/stdout/stderr piped and applies embedded runtime env sanitation.send_requestwrites newline-delimited JSON-RPC viawriteln!(process.stdin, "{}", request_json)and reads one stdout line withread_line.MCP_INITIALIZE_TIMEOUTbounds the initialize handshake at 15s.read_line.Playwright stealth MCP server
mcp-servers/playwright-stealth/src/index.tsStdioServerTransportfrom@modelcontextprotocol/sdk/server/stdio.js.main()connects stdio first, then logs:The #1921 fix moved browser detection after
server.connect, which fixed one startup timing issue, but it did not prove compatibility with the Content-Length framing used by the Prophet gateway.Cold-start tests
mcp-servers/playwright-stealth/src/__tests__/cold_start.test.tsinitializehandshake with the Prophet gateway framing.Bundling path
scripts/prepare-mcp-servers.tsandsrc-tauri/tauri.conf.jsonprepare:mcp-serversbuilds the TypeScript server and copies dependencies with hoisted node_modules.mcp-servers/playwright-stealth/.Settings migration
src/stores/settings.store.tsExternal caller path that exposes the bug
/Users/taariqlewis/Projects/Seren_Projects/seren-skills/prophet/prophet-arb-bot/scripts/otp_worker/playwright_mcp_gateway.pyRelevant prior commits audited
f1f77e80519247750ec67a3dfc330af9c14eb9cc/ feat: Replace Playwright MCP with stealth-enabled version #726: introduced the stealth Playwright MCP server.78d136c7cf33380414f0d07cc104af98c3ac0c3a/ fix: resolve playwright MCP failing due to dropped pnpm symlinks #862: fixed bundled pnpm symlink/node_modules issues. The current install has node_modules, so this is likely not the active failure.3f761dacadfb6aa1e64f28ad2272abb2e852d4ad/ fix: capture stderr from MCP processes for error diagnostics #861: added MCP stderr diagnostics. Current direct probe shows useful stderr, but the skill-facing timeout still loses framing/protocol detail.dee8e8fe2668bc37863b3e562a189754632ded1c/ fix(mcp): move local MCP stdio I/O off the main Tauri thread (fixes #1501) #1503: moved Desktop MCP stdio reads off the Tauri main thread and codified newline-delimitedread_linebehavior.2513a2f4014faf756f3440e06f862d91aac74af0/ fix(provider-runtime): strip VSCode/Cursor env vars before spawning embedded node (#1516) #1518: strips VSCode/Cursor/Electron env pollution before spawning node subprocesses. No evidence this is the current cause.211b82af4ba02b1cef244ed6783474471c5cc788/ feat(mcp): close #1918 — add playwright_get_cookie tool #1919: addedplaywright_get_cookie; likely not causal, but it touched the same MCP server entry/tool surface.0731715722be97f829fb9a6179b1c2b520f34b64/ fix(playwright-stealth): close #1921 — defer browser detection past MCP handshake #1922: fixed bug(playwright-stealth): eager top-level browser detection blocks MCP initialize handshake, prophet-arb-bot times out on cold start #1921 by deferring browser detection until afterserver.connect. This did not catch the Content-Length framed caller path.Root-cause hypothesis
There is a stdio transport/framing mismatch between at least two supported callers:
playwright-stealthbinary answers the first style and times out on the second.Because the server logs readiness on stderr and emits no stdout under Content-Length framing, the operator only sees
Timed out waiting for response from playwright-stealth MCP, which makes this look like a dead binary even though the binary is running.Proposed fix
playwright-stealthserver to that contract, or add an adapter/dual-framing layer if both callers must remain supported.mcp-servers/playwright-stealth/dist/index.jsand sends the exact initialize frame used by the Prophet Python gateway.src-tauri/src/mcp.rsif the canonical contract becomes Content-Length framing./Applications/SerenDesktop.app/Contents/Resources/mcp-servers/playwright-stealth/dist/index.jsor the equivalent release resource path before declaring a release good.Acceptance criteria
Timed out waiting for response from playwright-stealth MCPwhen using the Seren Desktop packaged binary.Duplicate check
Searched GitHub issues for
repo:serenorg/seren-desktop playwright-stealth Content-Length initialize timeout in:title,body; no matching open or closed issue was returned at filing time.