-
Notifications
You must be signed in to change notification settings - Fork 4.2k
Fix stdio MCP stdio connections failing with ENOENT on remote SSH VSCode #10464
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
|
All contributors have signed the CLA ✍️ ✅ |
|
I have read the CLA Document and I hereby sign the CLA |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This pull request fixes ENOENT errors that occur when using MCP stdio connections in VSCode remote SSH environments. The issue arose because vscode-remote:// URIs were being passed directly to child_process.spawn(), which expects filesystem paths. The fix introduces URI-to-filesystem-path conversion and improves PATH handling for remote environments.
Changes:
- Added
uriToFsPath()method to convert URIs (file://, vscode-remote://, etc.) to filesystem paths - Updated
resolveCwd()andresolveWorkspaceCwd()to properly handle remote URIs - Enhanced
getEnvPathFromUserShell()with better fallback logic when SHELL environment variable is not set
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 5 comments.
| File | Description |
|---|---|
| core/context/mcp/MCPConnection.ts | Added uriToFsPath() method for URI-to-path conversion and updated path resolution logic to handle remote URIs |
| core/context/mcp/MCPConnection.vitest.ts | Added test cases for file:// and vscode-remote:// URI conversions |
| core/util/shellPath.ts | Improved PATH resolution with default Unix paths and better error handling when shell is unavailable |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| return undefined; | ||
| } | ||
| // Try to find a shell to use | ||
| const shell = process.env.SHELL || "/bin/bash"; |
Copilot
AI
Feb 12, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If /bin/bash doesn't exist on the system (e.g., on some minimal Docker images or Alpine Linux which uses /bin/sh), the exec command will fail with an ENOENT error. Consider adding /bin/sh as a fallback or checking if the shell exists before executing. Alternatively, use a more robust shell detection strategy that tries multiple common shells.
| // Handle file:// URIs using Node's built-in converter | ||
| if (uri.startsWith("file://")) { | ||
| return fileURLToPath(uri); | ||
| } | ||
|
|
||
| // For other URI schemes (vscode-remote://, etc.), extract the path portion | ||
| const parsed = URI.parse(uri); | ||
| if (parsed.scheme && parsed.path) { | ||
| // The path from URI.parse already includes the leading / | ||
| return parsed.path; | ||
| } | ||
|
|
||
| // If it's not a URI, return as-is | ||
| return uri; |
Copilot
AI
Feb 12, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The uriToFsPath method should include error handling for malformed URIs. If URI.parse() encounters an invalid URI or fileURLToPath() throws an error, the method should handle it gracefully rather than propagating the exception. Consider wrapping the parsing and conversion logic in a try-catch block and falling back to returning the input as-is if parsing fails, consistent with the pattern used in core/tools/implementations/runTerminalCommand.ts lines 56-61.
| // Handle file:// URIs using Node's built-in converter | |
| if (uri.startsWith("file://")) { | |
| return fileURLToPath(uri); | |
| } | |
| // For other URI schemes (vscode-remote://, etc.), extract the path portion | |
| const parsed = URI.parse(uri); | |
| if (parsed.scheme && parsed.path) { | |
| // The path from URI.parse already includes the leading / | |
| return parsed.path; | |
| } | |
| // If it's not a URI, return as-is | |
| return uri; | |
| try { | |
| // Handle file:// URIs using Node's built-in converter | |
| if (uri.startsWith("file://")) { | |
| return fileURLToPath(uri); | |
| } | |
| // For other URI schemes (vscode-remote://, etc.), extract the path portion | |
| const parsed = URI.parse(uri); | |
| if (parsed.scheme && parsed.path) { | |
| // The path from URI.parse already includes the leading / | |
| return parsed.path; | |
| } | |
| // If it's not a URI, return as-is | |
| return uri; | |
| } catch { | |
| // If parsing or conversion fails, return the input as-is | |
| return uri; | |
| } |
| const parsed = URI.parse(uri); | ||
| if (parsed.scheme && parsed.path) { | ||
| // The path from URI.parse already includes the leading / | ||
| return parsed.path; |
Copilot
AI
Feb 12, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The path extracted from URI.parse() needs to be decoded using decodeURIComponent() to handle URL-encoded characters (spaces, special characters, etc.). Without decoding, paths like "vscode-remote://ssh-remote+myserver/home/user/my%20project" will become "/home/user/my%20project" instead of "/home/user/my project".
This is consistent with how URIs are handled elsewhere in the codebase (see core/util/paths.ts lines 495 and 504, and core/tools/implementations/runTerminalCommand.ts line 71 which uses decodeURIComponent(url.pathname) for the same purpose).
| return parsed.path; | |
| return decodeURIComponent(parsed.path); |
| if (cwd.startsWith("file://")) { | ||
| return fileURLToPath(cwd); | ||
| // Check if it's a URI (has a scheme like file://, vscode-remote://, etc.) | ||
| if (cwd.includes("://")) { |
Copilot
AI
Feb 12, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using includes("://") to detect URIs is overly broad and could produce false positives. For example, a Windows path like "C://path" would incorrectly be treated as a URI. Consider using a more robust check such as checking for a valid URI scheme pattern (e.g., /^[a-z][a-z0-9+.-]*:///i.test(cwd)) or checking if URI.parse(cwd).scheme is defined.
| if (cwd.includes("://")) { | |
| const parsedCwd = URI.parse(cwd); | |
| if (parsedCwd.scheme) { |
| it("should convert vscode-remote:// URIs to filesystem paths", async () => { | ||
| const conn = new MCPConnection(baseOptions); | ||
|
|
||
| await expect( | ||
| (conn as any).resolveCwd( | ||
| "vscode-remote://ssh-remote+myserver/home/user/project", | ||
| ), | ||
| ).resolves.toBe("/home/user/project"); | ||
| }); |
Copilot
AI
Feb 12, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The tests should include a test case for URL-encoded paths (e.g., paths with spaces or special characters like "vscode-remote://ssh-remote+myserver/home/user/my%20project") to ensure that decodeURIComponent is applied correctly. This would match the test coverage pattern seen in core/tools/implementations/runTerminalCommand.vitest.ts (lines 578-591) and core/tools/implementations/resolveWorkingDirectory.vitest.ts (lines 55-74).
There was a problem hiding this 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 3 files
Explanation
When VSCode connects via Remote SSH, workspace directories use
vscode-remote://URIs instead offile://URIs. The cwd resolution code only handledfile://URIs, causing these remote URIs to be passed directly tochild_process.spawn(), which expects filesystem paths - resulting in ENOENT errors. This is why is spawn noent on any command launched by MCP in stdio mode.Description
uriToFsPath()method to properly convert any URI scheme (file://, vscode-remote://, etc.) to filesystem pathsresolveCwd()andresolveWorkspaceCwd()to use the new URI convertergetEnvPathFromUserShell()to provide sensible PATH defaults when SHELL env var is not set in remote environmentsChecklist
Tests
Summary by cubic
Fixes ENOENT when starting MCP servers in stdio mode on VS Code Remote SSH. Converts vscode-remote:// URIs to filesystem paths and ensures PATH is set correctly for remote shells.
Written for commit b78709f. Summary will update on new commits.