Skip to content

fix: preserve quoted args with spaces when launching via CLI#1162

Open
petereon wants to merge 1 commit intomodelcontextprotocol:mainfrom
petereon:fix/preserve-quoted-cli-args
Open

fix: preserve quoted args with spaces when launching via CLI#1162
petereon wants to merge 1 commit intomodelcontextprotocol:mainfrom
petereon:fix/preserve-quoted-cli-args

Conversation

@petereon
Copy link
Copy Markdown

@petereon petereon commented Mar 27, 2026

Summary

Fixes a bug where CLI arguments containing spaces lose their quoting when passed through start.js to the proxy server. The root cause is a single Array.join(" ") call that collapses the args array into a flat string, making multi-word arguments indistinguishable from multiple single-word arguments by the time shellParseArgs runs in the proxy.

Note: Inspector V2 is under development to address architectural and UX improvements. During this time, V1 contributions should focus on bug fixes and MCP spec compliance. See CONTRIBUTING.md for more details.

Type of Change

  • Bug fix (non-breaking change that fixes an issue)
  • New feature (non-breaking change that adds functionality)
  • Documentation update
  • Refactoring (no functional changes)
  • Test updates
  • Build/CI improvements

Changes Made

Root cause

client/bin/start.js joined the args array with a plain space before passing it to the server process:

// Before — loses token boundaries
[`--args=${mcpServerArgs.join(" ")}`];

This transforms ["--description", "get todays date"] into "--description get todays date". From that point on, the three words are indistinguishable, and shellParseArgs in the proxy correctly splits them — producing the wrong result.

Fix

client/bin/start.js — serialize as a JSON array instead:

// After — preserves token boundaries
[`--args=${JSON.stringify(mcpServerArgs)}`];

server/src/index.ts/config endpoint — convert the JSON array back to a properly shell-quoted string for the client UI. shell-quote (already a dependency) is used to produce e.g. --description 'get todays date', which shellParseArgs in the proxy correctly parses back to a single element. Legacy plain shell strings (from direct server invocations) pass through unchanged.

server/src/index.tscreateTransport() — defensive hardening: try JSON.parse on query.args first; if it is a valid array use it directly, otherwise fall back to shellParseArgs. This handles any direct API callers that send a JSON array.

server/__tests__/args-parsing.test.ts (new) — 15 vitest tests covering the full round-trip and each conversion step in isolation.

Related Issues

N/A

Testing

  • Tested in UI mode
  • Tested in CLI mode
  • Tested with STDIO transport
  • Tested with SSE transport
  • Tested with Streamable HTTP transport
  • Added/updated automated tests
  • Manual testing performed

Test Results and/or Instructions

Automated tests (cd server && npm test):

 ✓ __tests__/args-parsing.test.ts (15 tests) 4ms
 Test Files  1 passed (1)
      Tests  15 passed (15)

Manual reproduction (before fix):

bunx @modelcontextprotocol/inspector sh2mcp \
  --tool date --description 'get todays date' --command date

Proxy logs showed:

Query parameters: {
  "command": "sh2mcp",
  "args": "--tool date --description get todays date --command date"
}
STDIO transport: command=sh2mcp,
  args=--tool,date,--description,get,todays,date,--command,date

After fix: "get todays date" appears as a single element — 5 args total instead of 7.

Checklist

  • Code follows the style guidelines (ran npm run prettier-fix)
  • Self-review completed
  • Code is commented where necessary
  • Documentation updated (README, comments, etc.)

Breaking Changes

None. Legacy invocations that pass --args="..." as a plain shell string to the server directly continue to work — the /config endpoint detects whether the value is a JSON array or a plain string and handles each case accordingly.

Additional Context

The data flow through the pipeline (showing where corruption occurred):

process.argv   ["--description", "get todays date"]   ✓ correct
start.js       mcpServerArgs.join(" ")                 ✗ was losing boundary
server --args  "--description get todays date"         ✗ was mangled
/config        defaultArgs: "...get todays date..."    ✗ was mangled
client state   args = "...get todays date..."          ✗ was mangled
shellParseArgs ["get", "todays", "date"]               ✗ was wrong

After fix:
start.js       JSON.stringify(mcpServerArgs)            ✓ preserves boundary
/config        shellQuoteArgs(parsed)                   ✓ "... 'get todays date' ..."
shellParseArgs ["get todays date"]                      ✓ correct

Args containing spaces (e.g. --description 'get todays date') were
silently split into multiple tokens by the proxy, breaking MCP servers
that rely on multi-word argument values.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant