fix(browser): add MCP timeout guard to prevent fs hangs#233
fix(browser): add MCP timeout guard to prevent fs hangs#233marcelsafin wants to merge 5 commits intoHKUDS:mainfrom
Conversation
|
@maintainers Could you switch requested reviewer to Codex for this PR? Copilot review is currently unavailable on our side. |
marcelsafin
left a comment
There was a problem hiding this comment.
Codex review pass completed.
Result: No blocking issues found in this PR.
Checked:
- Timeout guard is applied to init + tool calls in daemon and non-daemon paths
- Error messaging is actionable for token/server reachability problems
- New tests cover default/invalid/min timeout parsing and timeout exception path
- Registry version bump aligns with behavior and docs changes
Residual risk: low (mainly environment-dependent DOMShell runtime behavior).
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a5c940da61
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@codex address that feedback. Please ensure daemon timeout RuntimeError is propagated and does not trigger automatic non-daemon retry/reissue. |
|
To use Codex here, create an environment for this repo. |
|
Addressed the previous Codex feedback in the latest commit. @codex review |
|
To use Codex here, create an environment for this repo. |
|
@codex review |
|
@codex review latest head ( |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 3bcd80c7e9
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@codex review |
|
@codex review latest head |
|
@maintainers Status update: latest Codex P1 feedback is addressed in
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: cd55247595
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@codex review latest head |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 5338d01d82
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| except Exception as e: | ||
| except MCPToolTimeoutError: | ||
| # Do not auto-retry this tool call, but reset daemon for future recovery. | ||
| await _stop_daemon() |
There was a problem hiding this comment.
Bound daemon reset in timeout handler
Calling await _stop_daemon() directly in the MCPToolTimeoutError path can reintroduce an indefinite hang when the timeout was caused by a wedged daemon transport: _stop_daemon() awaits __aexit__ calls without any timeout, so cleanup itself may block forever and the tool never returns the timeout error. This means the new timeout-recovery path can still stall user commands under the same unresponsive-daemon conditions it is meant to recover from.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| tool_name, | ||
| ) | ||
| return result | ||
| except RuntimeError: |
There was a problem hiding this comment.
In _call_tool, except RuntimeError: raise is overly broad and will allow unrelated RuntimeErrors from session.initialize() / session.call_tool() to bypass the helpful wrapping RuntimeError("DOMShell MCP call failed...") message below. Consider narrowing this to only pass through MCPToolTimeoutError (or explicitly re-raising that type) while still wrapping other exceptions to preserve consistent, actionable error context for non-timeout failures.
| except RuntimeError: | |
| except MCPToolTimeoutError: |
| await _await_with_timeout( | ||
| _daemon_session.call_tool("domshell_focus", {"name": path}), | ||
| "domshell_focus (daemon)", | ||
| ) | ||
| return await _await_with_timeout( | ||
| _daemon_session.call_tool("domshell_type", {"text": text}), | ||
| "domshell_type (daemon)", | ||
| ) |
There was a problem hiding this comment.
type_text() uses the daemon session directly; if a daemon call times out (MCPToolTimeoutError), the daemon is not reset, unlike _call_tool which calls _stop_daemon() to allow recovery on subsequent commands. Consider catching MCPToolTimeoutError in the daemon branch here, calling _stop_daemon(), then re-raising so the next command can reconnect cleanly.
| await _await_with_timeout( | |
| _daemon_session.call_tool("domshell_focus", {"name": path}), | |
| "domshell_focus (daemon)", | |
| ) | |
| return await _await_with_timeout( | |
| _daemon_session.call_tool("domshell_type", {"text": text}), | |
| "domshell_type (daemon)", | |
| ) | |
| try: | |
| await _await_with_timeout( | |
| _daemon_session.call_tool("domshell_focus", {"name": path}), | |
| "domshell_focus (daemon)", | |
| ) | |
| return await _await_with_timeout( | |
| _daemon_session.call_tool("domshell_type", {"text": text}), | |
| "domshell_type (daemon)", | |
| ) | |
| except MCPToolTimeoutError: | |
| await _stop_daemon() | |
| raise |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| "name": "browser", | ||
| "display_name": "Browser", | ||
| "version": "1.0.0", | ||
| "version": "1.0.1", |
There was a problem hiding this comment.
registry.json bumps browser to 1.0.1, but the browser harness/package still reports 1.0.0 (e.g., browser/agent-harness/setup.py, cli_anything/browser/__init__.py, and the REPL banner in browser_cli.py). Please keep these versions in sync so users don’t see conflicting versions between cli-hub/registry and the installed package/CLI output.
| "version": "1.0.1", | |
| "version": "1.0.0", |
|
@copilot apply changes based on the comments in this thread |
1 similar comment
|
@copilot apply changes based on the comments in this thread |
…y, sync version Addresses Copilot/codex review feedback: - Bound _stop_daemon() __aexit__ calls with asyncio.wait_for so timeout recovery cannot itself hang on a wedged daemon transport (DOMSHELL_DAEMON_STOP_TIMEOUT, default 5s). - Narrow 'except RuntimeError: raise' to only pass through MCPToolTimeoutError; other RuntimeErrors are still wrapped with the helpful 'Ensure Chrome is running...' context. - Add MCPToolTimeoutError handling to type_text() daemon branch so a timeout there also resets the daemon, matching _call_tool behavior. - Bump browser package version 1.0.0 -> 1.0.1 to match registry.json (setup.py, browser/__init__.py, browser_cli.py REPL banner). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Addressed Copilot + codex review feedback in 7426cbb:
@copilot review |
|
Thanks for the timeout guard. The timeout parsing and cleanup shape look good, but I think the default 20 second wrapper around Please use a longer/separate initialization timeout or only start the shorter timeout once the package is already available. |
Description
Add timeout guards around browser MCP initialize/tool calls to prevent indefinite hangs for commands like
cli-anything-browser --json fs lswhen backend calls stall.Fixes #217
Type of Change
For New Software CLIs (in-repo)
<SOFTWARE>.mdSOP document exists at<software>/agent-harness/<SOFTWARE>.mdSKILL.mdexists atskills/cli-anything-<software>/SKILL.mdSKILL.mdexists atcli_anything/<software>/skills/SKILL.mdcli_anything/<software>/tests/test_core.pyare present and pass without backendcli_anything/<software>/tests/test_full_e2e.pyare presentREADME.mdincludes the new software (with link to harness directory)registry.jsonincludes an entry withsource_url: null(see Contributing guide)repl_skin.pyinutils/is an unmodified copy from the pluginFor New Software CLIs (standalone repo)
pip install <package-name>or apip install git+https://...URLSKILL.mdexists in the external reporegistry.jsonentry includessource_urlpointing to the external reporegistry.jsonentry includesskill_mdwith full URL to the external SKILL.mdinstall_cmdinregistry.jsonworks (tested locally)For Existing CLI Modifications
python3 -m pytest cli_anything/<software>/tests/test_core.py -vpython3 -m pytest cli_anything/<software>/tests/test_full_e2e.py -v(suite executes; skipped without external DOMShell env)registry.jsonentry is updated if version, description, or requirements changedGeneral Checklist
--jsonflag is supported on any new commandsfeat:,fix:,docs:,test:)Test Results