Linux-related stdio fixes and update version (#22)

- Modified `GetCommand` to return OS-specific commands and changed return type to `string?`.
- Improved `InitializeCommand` for better command initialization and logging.
- Cleaned up using directives in `Log.cs` and `SseClientTransport.cs`.
- Updated `CreateNoWindow` property in `StdioClientTransport` to be OS-dependent.
- Bumped version number in `mcpdotnet.csproj` to 1.0.1.1.
This commit is contained in:
Peder Holdgaard Pedersen 2025-02-19 19:52:56 +01:00 • committed by GitHub
commit 502a09d9fc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 16 additions and 22 deletions

View file

@ -166,7 +166,6 @@ while (true)
- Performance optimization - Performance optimization
- SSE server support - SSE server support
- Authentication - Authentication
- Linux/Mac support for stdio transport
## License ## License

View file

@ -135,7 +135,7 @@ public class McpClientFactory
return defaultValue; return defaultValue;
} }
private string GetCommand(McpServerConfig config) private string? GetCommand(McpServerConfig config)
{ {
if (config.TransportOptions == null || if (config.TransportOptions == null ||
string.IsNullOrEmpty(config.TransportOptions.GetValueOrDefault("command"))) string.IsNullOrEmpty(config.TransportOptions.GetValueOrDefault("command")))
@ -143,7 +143,7 @@ public class McpClientFactory
return config.Location!; return config.Location!;
} }
return $"cmd.exe"; return OperatingSystem.IsWindows() ? "cmd.exe" : config.TransportOptions?.GetValueOrDefault("command");
} }
/// <summary> /// <summary>
@ -151,29 +151,28 @@ public class McpClientFactory
/// </summary> /// </summary>
private void InitializeCommand(McpServerConfig config) private void InitializeCommand(McpServerConfig config)
{ {
string endpointName = $"Client ({config.Id}: {config.Name})"; string endpointName = $"Client ({config.Id}: {config.Name})";
// If the command is empty or already contains cmd.exe, we don't need to do anything // If the command is empty or already contains cmd.exe, we don't need to do anything
var command = config.TransportOptions?.GetValueOrDefault("command"); var command = config.TransportOptions?.GetValueOrDefault("command");
if (string.IsNullOrEmpty(command))
if (string.IsNullOrEmpty(command) || !OperatingSystem.IsWindows())
{ {
return; return;
} }
if (command.Contains("cmd.exe")) // On Windows, we need to wrap non-shell commands with cmd.exe /c
if (command.ToLowerInvariant().Contains("cmd.exe"))
{ {
_logger.SkippingShellWrapper(endpointName); _logger.SkippingShellWrapper(endpointName);
return; return;
} }
// If the command is not empty and does not contain cmd.exe, we need to inject /c {command} // If the command is not empty and does not contain cmd.exe, we need to inject /c {command} (usually npx or uvicorn)
if (config.TransportOptions != null && !string.IsNullOrEmpty(command)) // This is because the stdio transport will not work correctly if the command is not run in a shell
{ _logger.PromotingCommandToShellArgumentForStdio(endpointName, command, config.TransportOptions!.GetValueOrDefault("arguments") ?? "");
_logger.PromotingCommandToShellArgumentForStdio(endpointName, command, config.TransportOptions.GetValueOrDefault("arguments") ?? ""); config.TransportOptions!["arguments"] = config.TransportOptions.ContainsKey("arguments")
config.TransportOptions["arguments"] = config.TransportOptions.ContainsKey("arguments") ? $"/c {command} {config.TransportOptions["arguments"]}"
? $"/c {command} {config.TransportOptions["arguments"]}" : $"/c {command}";
: $"/c {command}";
}
} }
} }

View file

@ -1,5 +1,4 @@
using McpDotNet.Configuration; using McpDotNet.Protocol.Messages;
using McpDotNet.Protocol.Messages;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
namespace McpDotNet.Logging; namespace McpDotNet.Logging;

View file

@ -1,6 +1,4 @@
 using System.Text.Json;
using System.Diagnostics.Tracing;
using System.Text.Json;
using System.Text; using System.Text;
using McpDotNet.Protocol.Messages; using McpDotNet.Protocol.Messages;
using McpDotNet.Configuration; using McpDotNet.Configuration;
@ -9,7 +7,6 @@ using McpDotNet.Logging;
using System.Net.Http.Headers; using System.Net.Http.Headers;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
using McpDotNet.Utils.Json; using McpDotNet.Utils.Json;
using System.Threading;
namespace McpDotNet.Protocol.Transport; namespace McpDotNet.Protocol.Transport;

View file

@ -61,7 +61,7 @@ public sealed class StdioClientTransport : TransportBase, IClientTransport
RedirectStandardInput = true, RedirectStandardInput = true,
RedirectStandardOutput = true, RedirectStandardOutput = true,
RedirectStandardError = true, RedirectStandardError = true,
CreateNoWindow = true, CreateNoWindow = OperatingSystem.IsWindows(),
WorkingDirectory = _options.WorkingDirectory ?? Environment.CurrentDirectory, WorkingDirectory = _options.WorkingDirectory ?? Environment.CurrentDirectory,
}; };

View file

@ -9,7 +9,7 @@
<PropertyGroup> <PropertyGroup>
<!-- license and package properties --> <!-- license and package properties -->
<PackageId>mcpdotnet</PackageId> <PackageId>mcpdotnet</PackageId>
<Version>1.0.0.1</Version> <Version>1.0.1.1</Version>
<Authors>PederHP</Authors> <Authors>PederHP</Authors>
<Description>.NET client library for the Model Context Protocol (MCP)</Description> <Description>.NET client library for the Model Context Protocol (MCP)</Description>
<PackageProjectUrl>https://github.com/PederHP/mcpdotnet</PackageProjectUrl> <PackageProjectUrl>https://github.com/PederHP/mcpdotnet</PackageProjectUrl>