[BUG] Windows: Claude Code CLI corrupts stdio MCP password arguments when launching npx through cmd.exe
[BUG] Windows: Claude Code CLI corrupts stdio MCP password arguments when launching npx through cmd.exe
Preflight Checklist
- [x] I searched existing issues; this report is about argument corruption after launch, not the existing
npxENOENT/EINVALstartup failures. - [x] This is a single bug report.
- [x] I am using the latest stable version of Claude Code CLI available when this report was filed.
What's Wrong?
I use Claude Code CLI on native Windows with a local stdio MCP server to connect to a MySQL database. The MCP server is Anarkh-Lee/universal-db-mcp.
When the MCP configuration uses "command": "npx", Claude Code launches npx through a Windows cmd.exe wrapper. If a value in the MCP args array contains cmd.exe metacharacters, especially ^ or &, the value can be reparsed and changed before it reaches the MCP server.
I configured this MCP server through cc switch. In this setup, Claude Code CLI automatically adds the cmd.exe /d /s /c layer when it launches npx. Codex CLI does not add this extra cmd.exe wrapper for the same MCP use case, and the same database password works there without additional caret escaping.
This affects database passwords in particular. A password that works when passed directly to the database or through Codex CLI does not work unchanged when the same MCP server is launched by Claude Code CLI through npx.
The problem is not limited to passwords. The same launch path can affect API tokens, connection strings, and any other literal argument containing characters such as ^, &, |, <, >, or %.
Environment
- Claude Code CLI: latest stable release available when this report was filed
- Operating system: Windows 11, native Windows installation, not WSL
- Node.js: 20.x and 22.x
- MCP server:
universal-db-mcp - Database: MySQL
- Transport: stdio
- Terminal: PowerShell / Windows Terminal
Configuration
I configured the MCP server through cc switch with a configuration like this:
{
"mcpServers": {
"my-database": {
"command": "npx",
"args": [
"universal-db-mcp",
"--type", "mysql",
"--host", "localhost",
"--port", "3306",
"--user", "root",
"--password", "P@ss^&w0rd",
"--database", "test"
]
}
}
}
Steps to Reproduce
- Configure
universal-db-mcpthroughcc switchas a stdio MCP server usingcommand: "npx", and pass a password containing^and&in theargsarray. - Start Claude Code CLI on native Windows and load the MCP configuration.
- Try to use the MCP server to connect to MySQL.
- Observe that authentication fails even though the same database, username, and password work when tested independently.
- Add an additional caret as a temporary escape, for example changing the configured value from
P@ss^&w0rdtoP@ss^^&w0rd, then restart Claude Code. - In the affected setup, the additional caret can make the connection work, but the required number of carets depends on the characters in the value and on how many parsing layers are involved. More complex passwords can require multiple levels of escaping and remain inconsistent.
The child process command line observed from a separate PowerShell session has the following form:
cmd.exe /d /s /c "npx ^"universal-db-mcp^" ^"--type^" ^"mysql^" ... ^"--password^" ^"P@ss^^&w0rd^" ..."
The presence of the cmd.exe /d /s /c wrapper explains why the same literal args value is sensitive to cmd.exe escaping rules.
Observed Comparison
The same MCP server and password behave differently depending on the client launch path:
| Launch path | Wrapper | Result |
| --- | --- | --- |
| Claude Code CLI, MCP configured through cc switch with command: "npx" | cmd.exe /d /s /c around npx | Password must be manually caret-escaped; complex values can still be inconsistent |
| Codex CLI with the same MCP server and password | No additional cmd.exe wrapper | Works without adding caret escapes |
| Claude Code CLI with command: "node" and an absolute path to universal-db-mcp/dist/index.js | No npx.cmd/cmd.exe shim path | Works without adding caret escapes because the wrapper is bypassed |
This comparison points to the Claude Code Windows MCP process-launch path rather than the database server or the universal-db-mcp database adapter.
Expected Behavior
Each element of the MCP args array should reach the MCP subprocess as the same literal value configured by the user.
For example:
Configured password: P@ss^&w0rd
Received password: P@ss^&w0rd
The user should not need to know how many cmd.exe or transport parsing layers are involved, and a database password should not need to be changed to compensate for the client launch implementation.
Actual Behavior
Claude Code CLI launches the npx command through a cmd.exe /d /s /c wrapper by default in this setup. cmd.exe then interprets or reparses shell metacharacters in the serialized argument string. Codex CLI does not add this extra wrapper for the same MCP use case, so the same password reaches the MCP server without extra caret escaping.
As a result, the password reaching the MCP server is not reliably identical to the configured password. Depending on the exact combination of characters, a character can be consumed as an escape, remain as an extra caret, or cause the value to be split at a shell operator such as &.
Adding extra ^ characters can compensate for one parsing layer, but this is a fragile workaround. It becomes difficult to predict when Claude Code's own transport serialization and cmd.exe parsing both apply escaping.
Suspected Cause
The observed process command line is consistent with the Windows stdio MCP launch path converting the args array into shell text for cmd.exe, then allowing cmd.exe to parse that text again.
The exact internal Claude Code call site is not known, but the externally visible behavior is clear: a literal MCP argument is altered only when it goes through the Claude Code npx launch path on Windows.
This is also consistent with Node.js documentation for child_process.spawn and shell: true: when an args array is passed with a shell enabled, the arguments are concatenated rather than being handled as ordinary argv elements. Node identifies this pattern in the DEP0190 deprecation documentation.
Suggested Fix
Please audit the Windows stdio MCP process-launch path for npx and other .cmd/.bat shims.
The implementation should preserve the boundaries and literal values of all MCP arguments. It should avoid passing raw serialized arguments through cmd.exe; if a shell is unavoidable for a Windows batch shim, it needs a tested Windows-specific argument serializer that covers at least:
^,&,|,<,>, and%- spaces and empty arguments
- embedded double quotes
- trailing backslashes
- combinations of multiple metacharacters
- repeated parsing layers
Please add a regression test that launches a stdio probe with values such as:
simple
with space
a^b&c%d|e<>f
quote"inside
trailing\\
The test should inspect the actual arguments received by the child process, not only whether the MCP handshake succeeds.
Simply enabling shell: true without correctly escaping the arguments would not solve this issue; it would preserve the same class of corruption and may introduce the security risk described by Node's DEP0190 warning.
Workaround
A temporary workaround is to add extra caret escaping to the password value, but this depends on the number of parsing layers and is not reliable for arbitrary passwords.
A more reliable transport workaround is to bypass npx and launch the JavaScript entry point with the real node.exe executable:
{
"mcpServers": {
"my-database": {
"command": "node",
"args": [
"C:\\path\\to\\node_modules\\universal-db-mcp\\dist\\index.js",
"--type", "mysql",
"--host", "localhost",
"--port", "3306",
"--user", "root",
"--password", "P@ss^&w0rd",
"--database", "test"
]
}
}
}
This bypasses the npx.cmd/cmd.exe layer and preserves the argument in the direct node.exe launch. The path is machine-specific, and putting a password in args can still expose it in the child process command line; this workaround addresses argument corruption, not credential storage or process-list visibility.
Related Issues and References
- #58510 covers Windows stdio MCP servers using bare
npxthat fail to start withspawn ENOENT. This report covers a different failure mode: the MCP process starts, but its argument value is corrupted. - Node.js DEP0190 documentation documents the risk of passing an
argsarray together withshell: truewithout argument escaping. - VS Code PR #314642 provides relevant reference material for escaping arguments when launching MCP subprocesses through
cmd.exe; it is not a Claude Code fix.
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗