fix(mcp): give stdio servers the environment, and let builtins reconnect - #5770
Open
MummIndia wants to merge 1 commit into
Open
fix(mcp): give stdio servers the environment, and let builtins reconnect#5770MummIndia wants to merge 1 commit into
MummIndia wants to merge 1 commit into
Conversation
Two defects that combine to make the built-in browser MCP server
unusable, each of which hides the other.
`_connect_stdio` built `env={**os.environ, **env} if env else None`.
`None` does not mean "inherit the parent environment" — the MCP SDK
substitutes a minimal default one. Callers that pass an env inherit
everything, which is why the Python builtins work: they pass
`builtin_python_env(base_dir)`. The NPX browser server passes nothing,
so it loses the entire container environment, including
PLAYWRIGHT_BROWSERS_PATH. It then looks for browsers in the default
cache and reports `Browser "firefox" is not installed` — with the
browser sitting one directory away, which is what makes this so hard to
place.
Second, a stdio session can disappear without the process dying: the
teardown races across asyncio tasks. `call_tool` returned early on a
missing session, and the existing recovery only ran when a call raised
— which presupposes a session. A missing one was therefore terminal,
even though reconnecting would have fixed it. Reconnection is now
attempted in that case too.
`_reconnect_builtin` also excluded the browser outright. It tested
membership against `_BUILTIN_SERVERS`, the Python-server dict, while
`is_builtin()` counts the NPX servers as builtins as well — so the one
server most likely to need a restart was the one that could never get
one. It now handles both kinds.
Together these mean a browser server that dropped mid-session stayed
down for the rest of the process lifetime, and a fresh one started
without the environment it needs. Verified on Docker/Windows: the
server reconnects on demand and reports its 30 tools, and
`browser_navigate` returns exit_code=0 against a real URL.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Two defects that combine to make the built-in browser MCP server unusable, each of
which hides the other.
_connect_stdiobuiltenv={**os.environ, **env} if env else None.Nonedoes notmean "inherit the parent environment" — the MCP SDK substitutes a minimal default one.
Callers that pass an env inherit everything, which is why the Python builtins work:
they pass
builtin_python_env(base_dir). The NPX browser server passes nothing, so itloses the entire container environment including
PLAYWRIGHT_BROWSERS_PATH. It thenlooks for browsers in the default cache and reports
Browser "firefox" is not installed— with the browser sitting one directory away, which is what makes thishard to place.
Second, a stdio session can disappear without the process dying: the teardown races
across asyncio tasks.
call_toolreturned early on a missing session, and the existingrecovery only ran when a call raised — which presupposes a session. A missing one was
therefore terminal, even though reconnecting would have fixed it. Reconnection is now
attempted in that case too.
_reconnect_builtinalso excluded the browser outright: it tested membership against_BUILTIN_SERVERS, the Python-server dict, whileis_builtin()counts the NPX serversas builtins as well. It now handles both kinds.
Target branch
dev, notmain.Linked Issue
Fixes #5769
Type of Change
Checklist
devdocker compose up) and verified the change works end-to-end.How to Test
PLAYWRIGHT_BROWSERS_PATHpoints somewhere other than thedefault cache, with the built-in browser server enabled.
Browser "firefox" is not installed.Confirm the env is the cause by printing
os.environinside the spawned server.browser_navigateagainst a real URL returnsexit_code=0.tool. Before, every subsequent call returns
MCP server not connected: builtin_browserfor the process lifetime. After, thelog shows
No session for builtin builtin_browser; attempting reconnectfollowed byReconnected builtin MCP server: Built-in: Browser, and the call succeeds.Visual / UI changes
None.
src/mcp_manager.pyonly — transport and lifecycle, no rendering path.