Repository navigation
fix: resolve Windows command shims for stdio MCP servers - #1691
Open
pinqionghongbai wants to merge 1 commit into
Open
pinqionghongbai wants to merge 1 commit into
pinqionghongbai wants to merge 1 commit into
Conversation
On Windows, npm installs its command-line tools as batch shims
(npx.cmd, npm.cmd) rather than PE executables. ProcessBuilder delegates
to Win32 CreateProcess, which appends only .exe to an extension-less
command and never consults PATHEXT. A stdio server configured with
command("npx") therefore fails with CreateProcess error=2 even though
the same command works when typed into a shell -- the shell does the
PATHEXT lookup, and CreateProcess does not.
That failure never reaches the caller. No subprocess is ever spawned,
so the MCP client waits out its initialization timeout instead of
reporting anything. Measured on Windows 11 with the mcpfilesystem
sample: 900 s (3 attempts x 5-minute timeout) of silence before a
McpToolLoadingException.
DefaultMcpTransportBuilder now resolves the command against PATH and
PATHEXT before handing it to StdioClientTransport, mirroring
shutil.which as used by the Python MCP SDK: a command that already
carries a PATHEXT extension is tried as-is, anything else is tried with
each extension appended, in PATHEXT order. Non-Windows platforms, and
commands that are already paths, are untouched.
The lookup matches case-insensitively and returns the name as spelled
on disk, so the result does not depend on the case sensitivity of the
filesystem it runs on.
Fixes google#1686.
Contributor
|
Hi @pinqionghongbai, We appreciate your contribution and the effort you put into this pull request. It is currently under review by our team. We will notify you if any additional details are needed. |
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.
Fixes #1686.
Problem
On Windows,
McpToolsetconfigured with an npm-shim command (command("npx")) never starts asubprocess.
ProcessBuilderdelegates to Win32CreateProcess, which appends only.exeto anextension-less command and never consults
PATHEXT. npm installs its CLI tools as batch shims(
npx.cmd,npm.cmd), soCreateProcessfails witherror=2even thoughnpxworks fine in ashell — the shell does the
PATHEXTlookup,CreateProcessdoes not.The failure is invisible to callers: no process is ever spawned, so the MCP client waits out
initializationTimeout(default 5 min) and retries (MAX_RETRIES = 3, i.e. 3 total attempts).Measured on the reporter's machine with
adk-java1.13.0: 900,857 ms of silence, thenMcpToolLoadingException, withnode.exenever started.Fix
Resolve the command against
PATH/PATHEXTbefore handingServerParameterstoStdioClientTransport, mirroringshutil.whichas used by the Python MCP SDK — which is whereget_windows_executable_commandalready does this for the Python transports. This keeps the Javaside aligned with the other SDKs, per CONTRIBUTING.
New package-private helper
WindowsExecutableResolver:PATHEXTextension is tried as-is;PATHEXTextension appended, inPATHEXTorder;npx.cmd, not thenpx.CMDthatconcatenating
PATHEXTwould produce);/or\), and lookup misses all return the commandunchanged, so
ProcessBuilderstill surfaces its own, more useful error.DefaultMcpTransportBuilder.build()applies the resolver only forServerParametersand rebuildsthe parameters with the caller's args and env intact.
Measured result
Same machine, same repro, bare
command("npx"):npxMcpToolLoadingException,node.exenever startednpxnpx.cmdTests
WindowsExecutableResolverTest— 14 cases: non-Windows passthrough, explicit paths, shimresolution,
PATHEXTordering, multiplePATHentries, not-found passthrough, missingPATHEXTfallback, customPATHEXT, case-insensitive OS name.DefaultMcpTransportBuilderTest— 4 new cases: args/env survive the command substitution (runson every platform), the resolved command reaches the transport, and a Windows+Node-gated
end-to-end shim assertion.
coresuite: 2609 tests, 0 failures, 0 errors, 30 skipped (pre-existing assumption-basedskips). With
-Dos.name=Linuxthe Windows-only case is skipped and the rest still pass.mvn -Prelease clean package -pl core -am— the command CI runs — passes, including the javadocjar (
doclint=all,-missing).mvn fmt:check(all modules): 0 non-complying.Note on placement
The resolution arguably belongs in the upstream Java MCP SDK's
StdioClientTransport(that iswhere the Python SDK does it). It is placed in
DefaultMcpTransportBuilderso ADK users get thefix without waiting on an upstream release; if you would rather move it upstream, it can be lifted
as-is.