Skip to content

StdioClientTransportOptions is failing with space in Command #1601

@manasajwalia

Description

@manasajwalia

Describe the bug

On Windows, StdioClientTransport wraps every non-cmd.exe command as cmd.exe /c <Command> <Arguments...>. When Command is an absolute path that contains a space (e.g. anything under C:\Program Files\...) and Arguments is non-empty, the resulting command line confuses cmd.exe's /c quote-handling rules: cmd strips the outer quotes around the executable path, splits on the first space, and tries to launch a non-existent program. The MCP server is never started.

The relevant code is in [src/ModelContextProtocol.Core/Client/StdioClientTransport.cs](https://github.com/modelcontextprotocol/csharp-sdk/blob/main/src/ModelContextProtocol.Core/Client/StdioClientTransport.cs):

if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) &&
    !string.Equals(Path.GetFileName(command), "cmd.exe", StringComparison.OrdinalIgnoreCase))
{
    arguments = arguments is null or [] ? ["/c", command] : ["/c", command, ..arguments];
    command = "cmd.exe";
}

Per cmd /?, when /c is followed by more than two quote characters, cmd strips the first and last quote and re-parses the middle. Because ProcessStartInfo.ArgumentList correctly quotes both the space-containing path and any further argument, you always end up with 4+ quote characters and trigger this stripping behavior. The /s switch plus an extra outer pair of quotes around the whole command-and-args region would prevent this, but the SDK does not emit either.

To Reproduce

Steps to reproduce the behavior:

  1. Place any working stdio MCP server executable at a path containing a space, e.g. C:\Program Files\MssqlMcp\server.exe.
  2. Create a client with at least one argument:
    var options = new StdioClientTransportOptions
    {
        Name = "MssqlMcp",
        Command = @"C:\Program Files\MssqlMcp\server.exe",
        Arguments = [connectionString]
    };
    var transport = new StdioClientTransport(options);
    var client = await McpClient.CreateAsync(transport);
  3. Observe that CreateAsync fails — the child process either exits immediately or never starts.
  4. Move/copy the same executable to a space-free path (e.g. C:\Tools\MssqlMcp\server.exe), update Command, and the exact same code works.

Expected behavior

StdioClientTransport should launch the configured executable regardless of whether its path contains spaces, just as it does for paths without spaces. The Windows cmd /c wrapping should be transparent to the caller.

Logs

Trace-level log from StdioClientTransport shows the wrapped invocation; the spawned cmd.exe produces output like:

'C:\Program' is not recognized as an internal or external command,
operable program or batch file.

(Exact message varies depending on whether the path's first space-separated token happens to match an existing file.)

Additional context

  • Platform: Windows (any version). Not reproducible on Linux/macOS — the wrapping branch is Windows-only.
  • Reproduces whenever Path.GetFileName(Command) != "cmd.exe" and the path contains a space and Arguments is non-empty. With zero arguments, cmd's "exactly two quotes around an executable" special case happens to make it work, which is why this bug is intermittent and easy to miss in samples (npx, dotnet, uvicorn all live on space-free PATH entries).

Workarounds users currently have to apply:

  • Install/copy the server to a space-free directory.

A proper fix would be for the SDK to either (a) build the joined-and-quoted command string itself when wrapping with cmd (e.g. ["/d", "/s", "/c", $"\"\"{command}\" {joinedArgs}\""]) rather than relying on ArgumentList's per-arg quoting, or (b) skip the cmd wrapping entirely when Command resolves to a real executable file — the original justification in the source comment ("usually npx or uvicorn") only applies to PATH-resolved batch/script commands, not absolute .exe paths.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions