Skip to content

Conversation

@fradav
Copy link

@fradav fradav commented Feb 12, 2026

Explanation

When VSCode connects via Remote SSH, workspace directories use vscode-remote:// URIs instead of file:// URIs. The cwd resolution code only handled file:// URIs, causing these remote URIs to be passed directly to child_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

  1. Added uriToFsPath() method to properly convert any URI scheme (file://, vscode-remote://, etc.) to filesystem paths
  2. Updated resolveCwd() and resolveWorkspaceCwd() to use the new URI converter
  3. Improved getEnvPathFromUserShell() to provide sensible PATH defaults when SHELL env var is not set in remote environments

Checklist

  • I've read the contributing guide
  • The relevant docs, if any, have been updated or created
  • The relevant tests, if any, have been updated or created

Tests

  • Added 3 new tests for remote URI handling
  • All existing tests pass
  • Verified with actual MCP servers on remote SSH connection

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.

  • Bug Fixes
    • Added uriToFsPath and used it in resolveCwd/resolveWorkspaceCwd to handle file:// and vscode-remote:// URIs.
    • Retrieved PATH from the user’s shell on non-Windows and WSL remotes, with sane defaults when SHELL is missing.
    • Added tests for URI conversion and remote workspace path resolution.

Written for commit b78709f. Summary will update on new commits.

@fradav fradav requested a review from a team as a code owner February 12, 2026 09:25
@fradav fradav requested review from RomneyDa and Copilot and removed request for a team February 12, 2026 09:25
@dosubot dosubot bot added the size:L This PR changes 100-499 lines, ignoring generated files. label Feb 12, 2026
@github-actions
Copy link

github-actions bot commented Feb 12, 2026

All contributors have signed the CLA ✍️ ✅
Posted by the CLA Assistant Lite bot.

@fradav
Copy link
Author

fradav commented Feb 12, 2026

I have read the CLA Document and I hereby sign the CLA

Copy link

Copilot AI left a 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() and resolveWorkspaceCwd() 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";
Copy link

Copilot AI Feb 12, 2026

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.

Copilot uses AI. Check for mistakes.
Comment on lines +455 to +468
// 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;
Copy link

Copilot AI Feb 12, 2026

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.

Suggested change
// 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;
}

Copilot uses AI. Check for mistakes.
const parsed = URI.parse(uri);
if (parsed.scheme && parsed.path) {
// The path from URI.parse already includes the leading /
return parsed.path;
Copy link

Copilot AI Feb 12, 2026

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).

Suggested change
return parsed.path;
return decodeURIComponent(parsed.path);

Copilot uses AI. Check for mistakes.
if (cwd.startsWith("file://")) {
return fileURLToPath(cwd);
// Check if it's a URI (has a scheme like file://, vscode-remote://, etc.)
if (cwd.includes("://")) {
Copy link

Copilot AI Feb 12, 2026

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.

Suggested change
if (cwd.includes("://")) {
const parsedCwd = URI.parse(cwd);
if (parsedCwd.scheme) {

Copilot uses AI. Check for mistakes.
Comment on lines +187 to +195
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");
});
Copy link

Copilot AI Feb 12, 2026

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).

Copilot uses AI. Check for mistakes.
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a 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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L This PR changes 100-499 lines, ignoring generated files.

Projects

Status: Todo

Development

Successfully merging this pull request may close these issues.

1 participant