-
Notifications
You must be signed in to change notification settings - Fork 432
Description
Describe the bug
When StdioClientTransport uses "cmd.exe /c" to invoke a command with arguments, it doesn't seem to work if an argument contains the ampersand character ("&"). Ran into this issue when I tried using StdioClientTransport to test the Dataverse MCP Server.
To Reproduce
The following code throws an exception:
var clientTransport = new StdioClientTransport(new StdioClientTransportOptions
{
Name = "DataverseMcpServer",
Command = "Microsoft.PowerPlatform.Dataverse.MCP",
Arguments = [
"--ConnectionUrl",
"https://make.powerautomate.com/environments/7c89bd81-ec79-e990-99eb-90d823595740/connections?apiName=shared_commondataserviceforapps&connectionName=91433eff0e204d9a96771a47117a7d48",
"--MCPServerName",
"DataverseMCPServer",
"--TenantId",
"ea59b638-3d02-4773-83a8-a7f8606da0b6",
"--EnableHttpLogging",
"true",
"--EnableMsalLogging",
"false",
"--Debug",
"false",
"--BackendProtocol",
"HTTP"
],
});
var client = await McpClientFactory.CreateAsync(clientTransport);
It is clear from the error log that it interprets the ampersand in argument 2 as a divider between two subsequent commands. Adding quotations around the argument it not helping either, since the MCP server in this case is not expecting this.
Expected behavior
The expectation is that the arguments specified this way should work, since this is the typical way that arguments are specified when configuring MCP Servers.
Logs
Exception has occurred: CLR/System.IO.IOException
An exception of type 'System.IO.IOException' occurred in System.Private.CoreLib.dll but was not handled in user code: 'MCP server process exited unexpectedly (exit code: 1)
Server's stderr tail:
Unhandled exception. System.ArgumentException: Invalid URL format. Missing connectionName.
at PowerPlatformMCPProxy.ConnectionManagement.UrlConnectionExtractor.ExtractUrlConnectionDetails() in C:__w\1\s\src\MCPServers\PowerPlatformMCPProxy\Clients\UrlConnectionExtractor.cs:line 38
at PowerPlatformMCPProxy.Runtime.MCPServerProxy.RunAsync() in C:__w\1\s\src\MCPServers\PowerPlatformMCPProxy\Runtime\MCPServerProxy.cs:line 58
at Program.
at Program.(String[] args)
'connectionName' is not recognized as an internal or external command,
operable program or batch file.'
at System.Threading.Channels.AsyncOperation
1.GetResult(Int16 token) at System.Threading.Channels.ChannelReader
1.d__12.MoveNext()at System.Threading.Channels.ChannelReader`1.d__12.System.Threading.Tasks.Sources.IValueTaskSource<System.Boolean>.GetResult(Int16 token)
at ModelContextProtocol.McpSession.d__23.MoveNext() in c:\Sandboxes\SemanticKernelMcp\csharp-
Additional context
The following update to StdioClientTransport.ConnectAsync seems to be a workaround, which uses powershell instead of cmd.exe. This requires that the ampersand is surrounded by quotations, like so:
public async Task<ITransport> ConnectAsync(CancellationToken cancellationToken = default)
{
string endpointName = Name;
Process? process = null;
bool processStarted = false;
string command = _options.Command;
IList<string>? arguments = _options.Arguments;
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) &&
!string.Equals(Path.GetFileName(command), "powershell.exe", StringComparison.OrdinalIgnoreCase) &&
!string.Equals(Path.GetFileName(command), "pwsh.exe", StringComparison.OrdinalIgnoreCase))
{
// On Windows, for stdio, we need to wrap non-shell commands in a shell.
var originalCommand = command;
command = "powershell.exe";
var allArgs = arguments is null or [] ? new List<string>() : new List<string>(arguments);
allArgs.Insert(0, originalCommand);
var commandLine = string.Join(" ", allArgs);
arguments = new[] { "-Command", commandLine };
}
...