${userHome} on Windows isn’t expanded the same as on macOS / Linux.
${userHome} in mcp.json resolves to the URI path, not the filesystem path, so on Windows it becomes /c:/Users/ and every stdio server using it fails with spawn ENOENT. macOS/Linux are unaffected because the two forms coincide there. MCPService.resolveVariables uses IConfigurationResolverService.resolveAsync, whose userHome is built with home.path instead of home.fsPath
Here is a similar bug that were found in the ecosystem:
Hey @douges! On Windows ${userHome} in mcp.json comes through as /c:/Users/... instead of C:\Users\..., so stdio servers using it can’t spawn. That’s on our side, not your setup (macOS/Linux match up, so they’re unaffected), and it’s an issue we’re tracking.
Your env-var switch is the right call: ${env:USERPROFILE} on Windows / ${env:HOME} on macOS/Linux, or the combined ${env:HOME}${env:USERPROFILE}/... for cross-platform. A leading ~/ in command/args also works if you prefer that form. I’ll post here when there’s an update.
@douges yes, a leading ~/ works on Windows. In command or an args entry it’s expanded from the OS home directory (the real C:\Users\you\... path), so ~/AppData/Local/Programs/cursor/Cursor.exe resolves correctly, unlike ${userHome} which comes through as the URI form. Two caveats: the ~ has to be at the start of the value, and the expander splits on spaces, so if a path has spaces the ${env:USERPROFILE} form is safer.
The ~/ shortcut only gets expanded when a workspace folder is present. Your Linux run went through the shared MCP process (the [Shared MCP process] line in your log), where ~ is passed through literally, so it tried to spawn ~/.paper/bin/paper and failed. So ~/ isn’t reliable across setups. The env-var form is the robust one: use ${env:HOME} on Linux, or your combined ${env:HOME}${env:USERPROFILE}/... which resolves to a real path on every platform. We’ve noted the ~/ gap alongside the original issue so it’s covered too.