Skip to content

Commit 404aa98

Browse files
committed
Document Windows stdio shell parsing
1 parent 514cf68 commit 404aa98

2 files changed

Lines changed: 20 additions & 0 deletions

File tree

docs/concepts/transports/transports.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@ The following table describes the key <xref:ModelContextProtocol.Client.StdioCli
4545
| `StandardErrorLines` | Callback for stderr output from the server process |
4646
| `Name` | Optional transport identifier for logging |
4747

48+
> [!WARNING]
49+
> **Windows command-shell parsing:** On Windows, <xref:ModelContextProtocol.Client.StdioClientTransport> launches every command other than `cmd.exe` through `cmd.exe /c`. Both `Command` and `Arguments` are therefore interpreted by the Windows command shell, even though `Arguments` is supplied as a list. For values without whitespace, the SDK applies limited caret-escaping for `&`, `^`, `>`, `<`, and `|`, but this is not a general-purpose defense against command injection. Do not build either property from untrusted input; validate or allowlist any externally derived values before launching a stdio server.
50+
4851
#### Environment variable inheritance
4952

5053
By default, the server process inherits **all** environment variables from the current process. This includes credentials, tokens, proxy settings, and internal configuration that might be sensitive or irrelevant to the server. When running third-party or untrusted MCP servers, consider disabling inheritance to prevent unintentional credential leakage:

src/ModelContextProtocol.Core/Client/StdioClientTransportOptions.cs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,13 @@ public sealed class StdioClientTransportOptions
9494
/// <summary>
9595
/// Gets or sets the command to execute to start the server process.
9696
/// </summary>
97+
/// <remarks>
98+
/// <para>
99+
/// On Windows, commands other than <c>cmd.exe</c> are launched through <c>cmd.exe /c</c>. As a result, the
100+
/// command is interpreted by the Windows command shell rather than passed directly to the child process. Do not
101+
/// construct this value from untrusted input.
102+
/// </para>
103+
/// </remarks>
97104
/// <exception cref="ArgumentException">The value is <see langword="null"/>, empty, or composed entirely of whitespace.</exception>
98105
public required string Command
99106
{
@@ -112,6 +119,16 @@ public required string Command
112119
/// <summary>
113120
/// Gets or sets the arguments to pass to the server process when it is started.
114121
/// </summary>
122+
/// <remarks>
123+
/// <para>
124+
/// On Windows, commands other than <c>cmd.exe</c> are launched through <c>cmd.exe /c</c>, so these values are
125+
/// interpreted by the Windows command shell even though they are supplied as a list. For values without
126+
/// whitespace, the transport applies limited caret-escaping for <c>&amp;</c>, <c>^</c>, <c>&gt;</c>, <c>&lt;</c>, and
127+
/// <c>|</c>, but callers must not rely on the list representation or that escaping as a general-purpose defense
128+
/// against command injection. Validate or allowlist any values derived from external input before including them
129+
/// here.
130+
/// </para>
131+
/// </remarks>
115132
public IList<string>? Arguments { get; set; }
116133

117134
/// <summary>

0 commit comments

Comments
 (0)