Remove warnings from build (#29)
* Remove warnings from build * More fixes. * Using const for transport types
This commit is contained in:
parent
80daa5b73d
commit
7d69499ad5
44 changed files with 277 additions and 263 deletions
|
|
@ -61,7 +61,7 @@ var config = new McpServerConfig
|
||||||
{
|
{
|
||||||
Id = "everything",
|
Id = "everything",
|
||||||
Name = "Everything",
|
Name = "Everything",
|
||||||
TransportType = "stdio",
|
TransportType = TransportTypes.StdIo,
|
||||||
TransportOptions = new Dictionary<string, string>
|
TransportOptions = new Dictionary<string, string>
|
||||||
{
|
{
|
||||||
["command"] = "npx",
|
["command"] = "npx",
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ try
|
||||||
builder.Services.AddSerilog();
|
builder.Services.AddSerilog();
|
||||||
builder.Services.AddMcpServer()
|
builder.Services.AddMcpServer()
|
||||||
.WithStdioServerTransport()
|
.WithStdioServerTransport()
|
||||||
.WithCallToolHandler(async (r, ct) => new McpDotNet.Protocol.Types.CallToolResponse());
|
.WithCallToolHandler((r, ct) => Task.FromResult(new McpDotNet.Protocol.Types.CallToolResponse()));
|
||||||
|
|
||||||
var app = builder.Build();
|
var app = builder.Build();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ public static class McpToolExtensions
|
||||||
{
|
{
|
||||||
public static IList<Anthropic.SDK.Common.Tool> ToAnthropicTools(this IEnumerable<McpDotNet.Protocol.Types.Tool> tools)
|
public static IList<Anthropic.SDK.Common.Tool> ToAnthropicTools(this IEnumerable<McpDotNet.Protocol.Types.Tool> tools)
|
||||||
{
|
{
|
||||||
List<Anthropic.SDK.Common.Tool> result = new();
|
List<Anthropic.SDK.Common.Tool> result = [];
|
||||||
foreach (var tool in tools)
|
foreach (var tool in tools)
|
||||||
{
|
{
|
||||||
var function = tool.InputSchema == null
|
var function = tool.InputSchema == null
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,10 @@
|
||||||
using McpDotNet.Client;
|
using Anthropic.SDK;
|
||||||
using McpDotNet.Configuration;
|
|
||||||
using Microsoft.Extensions.Logging.Abstractions;
|
|
||||||
using Anthropic.SDK;
|
|
||||||
using Anthropic.SDK.Messaging;
|
|
||||||
using Anthropic.SDK.Constants;
|
using Anthropic.SDK.Constants;
|
||||||
|
using Anthropic.SDK.Messaging;
|
||||||
|
using McpDotNet.Client;
|
||||||
|
using McpDotNet.Configuration;
|
||||||
|
using McpDotNet.Protocol.Transport;
|
||||||
|
using Microsoft.Extensions.Logging.Abstractions;
|
||||||
|
|
||||||
internal class Program
|
internal class Program
|
||||||
{
|
{
|
||||||
|
|
@ -19,7 +20,7 @@ internal class Program
|
||||||
{
|
{
|
||||||
Id = "everything",
|
Id = "everything",
|
||||||
Name = "Everything",
|
Name = "Everything",
|
||||||
TransportType = "stdio",
|
TransportType = TransportTypes.StdIo,
|
||||||
TransportOptions = new Dictionary<string, string>
|
TransportOptions = new Dictionary<string, string>
|
||||||
{
|
{
|
||||||
["command"] = "npx",
|
["command"] = "npx",
|
||||||
|
|
@ -85,7 +86,7 @@ internal class Program
|
||||||
|
|
||||||
foreach (var toolCall in res.ToolCalls)
|
foreach (var toolCall in res.ToolCalls)
|
||||||
{
|
{
|
||||||
var response = await client.CallToolAsync(toolCall.Name, toolCall.Arguments?.ToMCPArguments());
|
var response = await client.CallToolAsync(toolCall.Name, toolCall.Arguments?.ToMCPArguments() ?? []);
|
||||||
|
|
||||||
messages.Add(new Message(toolCall, response.Content[0].Text));
|
messages.Add(new Message(toolCall, response.Content[0].Text));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ public class McpAIFunction : AIFunction
|
||||||
protected async override Task<object?> InvokeCoreAsync(IEnumerable<KeyValuePair<string, object?>> arguments, CancellationToken cancellationToken)
|
protected async override Task<object?> InvokeCoreAsync(IEnumerable<KeyValuePair<string, object?>> arguments, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
// Convert arguments to dictionary format expected by mcpdotnet
|
// Convert arguments to dictionary format expected by mcpdotnet
|
||||||
Dictionary<string, object> argDict = new();
|
Dictionary<string, object> argDict = [];
|
||||||
foreach (var arg in arguments)
|
foreach (var arg in arguments)
|
||||||
{
|
{
|
||||||
if (arg.Value is not null)
|
if (arg.Value is not null)
|
||||||
|
|
@ -42,7 +42,7 @@ public class McpAIFunction : AIFunction
|
||||||
// Call the tool through mcpdotnet
|
// Call the tool through mcpdotnet
|
||||||
var result = await _client.CallToolAsync(
|
var result = await _client.CallToolAsync(
|
||||||
_tool.Name,
|
_tool.Name,
|
||||||
argDict.Count == 0 ? new() : argDict,
|
argDict.Count == 0 ? [] : argDict,
|
||||||
cancellationToken: cancellationToken
|
cancellationToken: cancellationToken
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -60,9 +60,9 @@ public class McpAIFunction : AIFunction
|
||||||
var properties = jsonSchema?.Properties;
|
var properties = jsonSchema?.Properties;
|
||||||
if (properties == null)
|
if (properties == null)
|
||||||
{
|
{
|
||||||
return new();
|
return [];
|
||||||
}
|
}
|
||||||
HashSet<string> requiredProperties = new(jsonSchema!.Required ?? []);
|
HashSet<string> requiredProperties = [.. jsonSchema!.Required ?? []];
|
||||||
return properties.Select(kvp =>
|
return properties.Select(kvp =>
|
||||||
new AIFunctionParameterMetadata(kvp.Key)
|
new AIFunctionParameterMetadata(kvp.Key)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
using McpDotNet.Client;
|
using McpDotNet.Client;
|
||||||
using McpDotNet.Configuration;
|
using McpDotNet.Configuration;
|
||||||
|
using McpDotNet.Protocol.Transport;
|
||||||
using Microsoft.Extensions.AI;
|
using Microsoft.Extensions.AI;
|
||||||
using Microsoft.Extensions.Logging.Abstractions;
|
using Microsoft.Extensions.Logging.Abstractions;
|
||||||
using OpenAI;
|
using OpenAI;
|
||||||
using OpenAI.Chat;
|
|
||||||
using SimpleToolsConsole;
|
using SimpleToolsConsole;
|
||||||
|
|
||||||
internal class Program
|
internal class Program
|
||||||
|
|
@ -20,7 +20,7 @@ internal class Program
|
||||||
{
|
{
|
||||||
Id = "everything",
|
Id = "everything",
|
||||||
Name = "Everything",
|
Name = "Everything",
|
||||||
TransportType = "stdio",
|
TransportType = TransportTypes.StdIo,
|
||||||
TransportOptions = new Dictionary<string, string>
|
TransportOptions = new Dictionary<string, string>
|
||||||
{
|
{
|
||||||
["command"] = "npx",
|
["command"] = "npx",
|
||||||
|
|
@ -66,9 +66,11 @@ internal class Program
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
// Create message list
|
// Create message list
|
||||||
IList<Microsoft.Extensions.AI.ChatMessage> messages = new List<Microsoft.Extensions.AI.ChatMessage>();
|
IList<Microsoft.Extensions.AI.ChatMessage> messages =
|
||||||
// Add a system message
|
[
|
||||||
messages.Add(new(ChatRole.System, "You are a helpful assistant, helping us test MCP server functionality."));
|
// Add a system message
|
||||||
|
new(ChatRole.System, "You are a helpful assistant, helping us test MCP server functionality."),
|
||||||
|
];
|
||||||
// If MCP server provides instructions, add them as an additional system message (you could also add it as a content part)
|
// If MCP server provides instructions, add them as an additional system message (you could also add it as a content part)
|
||||||
if (!string.IsNullOrEmpty(client.ServerInstructions))
|
if (!string.IsNullOrEmpty(client.ServerInstructions))
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ internal static class AIFunctionUtilities
|
||||||
["type"] = "object",
|
["type"] = "object",
|
||||||
["title"] = tool.Name,
|
["title"] = tool.Name,
|
||||||
["description"] = tool.Description ?? string.Empty,
|
["description"] = tool.Description ?? string.Empty,
|
||||||
["properties"] = tool.InputSchema?.Properties ?? new(),
|
["properties"] = tool.InputSchema?.Properties ?? [],
|
||||||
["required"] = tool.InputSchema?.Required ?? []
|
["required"] = tool.InputSchema?.Required ?? []
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ public class McpAIFunction : AIFunction
|
||||||
protected async override Task<object?> InvokeCoreAsync(IEnumerable<KeyValuePair<string, object?>> arguments, CancellationToken cancellationToken)
|
protected async override Task<object?> InvokeCoreAsync(IEnumerable<KeyValuePair<string, object?>> arguments, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
// Convert arguments to dictionary format expected by mcpdotnet
|
// Convert arguments to dictionary format expected by mcpdotnet
|
||||||
Dictionary<string, object> argDict = new();
|
Dictionary<string, object> argDict = [];
|
||||||
foreach (var arg in arguments)
|
foreach (var arg in arguments)
|
||||||
{
|
{
|
||||||
if (arg.Value is not null)
|
if (arg.Value is not null)
|
||||||
|
|
@ -44,7 +44,7 @@ public class McpAIFunction : AIFunction
|
||||||
// Call the tool through mcpdotnet
|
// Call the tool through mcpdotnet
|
||||||
var result = await _client.CallToolAsync(
|
var result = await _client.CallToolAsync(
|
||||||
_tool.Name,
|
_tool.Name,
|
||||||
argDict.Count == 0 ? new() : argDict,
|
argDict.Count == 0 ? [] : argDict,
|
||||||
cancellationToken: cancellationToken
|
cancellationToken: cancellationToken
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ namespace McpDotNet.Extensions.AI;
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class McpSessionScope : IAsyncDisposable
|
public class McpSessionScope : IAsyncDisposable
|
||||||
{
|
{
|
||||||
private readonly List<IMcpClient> _clients = new();
|
private readonly List<IMcpClient> _clients = [];
|
||||||
private bool _disposed;
|
private bool _disposed;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ var config = new McpServerConfig
|
||||||
{
|
{
|
||||||
Id = "everything",
|
Id = "everything",
|
||||||
Name = "Everything",
|
Name = "Everything",
|
||||||
TransportType = "stdio",
|
TransportType = TransportTypes.StdIo,
|
||||||
TransportOptions = new Dictionary<string, string>
|
TransportOptions = new Dictionary<string, string>
|
||||||
{
|
{
|
||||||
["command"] = "npx",
|
["command"] = "npx",
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
namespace McpDotNet.Client;
|
namespace McpDotNet.Client;
|
||||||
|
|
||||||
using McpDotNet.Protocol.Types;
|
|
||||||
using McpDotNet.Protocol.Messages;
|
using McpDotNet.Protocol.Messages;
|
||||||
|
using McpDotNet.Protocol.Types;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents an instance of the MCP client connecting to a specific server.
|
/// Represents an instance of the MCP client connecting to a specific server.
|
||||||
|
|
@ -35,12 +35,12 @@ public interface IMcpClient : IAsyncDisposable
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the handler for server sampling requests.
|
/// Gets or sets the handler for server sampling requests.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Func<CreateMessageRequestParams, CancellationToken, Task<CreateMessageResult>>? SamplingHandler { get; set; }
|
Func<CreateMessageRequestParams?, CancellationToken, Task<CreateMessageResult>>? SamplingHandler { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the handler for providing root URIs to servers.
|
/// Gets or sets the handler for providing root URIs to servers.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Func<ListRootsRequestParams, CancellationToken, Task<ListRootsResult>>? RootsHandler { get; set; }
|
Func<ListRootsRequestParams?, CancellationToken, Task<ListRootsResult>>? RootsHandler { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Establishes a connection to the server.
|
/// Establishes a connection to the server.
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
|
|
||||||
using McpDotNet.Protocol.Types;
|
|
||||||
using McpDotNet.Protocol.Transport;
|
|
||||||
using McpDotNet.Protocol.Messages;
|
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using McpDotNet.Logging;
|
|
||||||
using McpDotNet.Configuration;
|
using McpDotNet.Configuration;
|
||||||
|
using McpDotNet.Logging;
|
||||||
|
using McpDotNet.Protocol.Messages;
|
||||||
|
using McpDotNet.Protocol.Transport;
|
||||||
|
using McpDotNet.Protocol.Types;
|
||||||
using McpDotNet.Shared;
|
using McpDotNet.Shared;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace McpDotNet.Client;
|
namespace McpDotNet.Client;
|
||||||
|
|
||||||
|
|
@ -46,7 +46,8 @@ internal class McpClient : McpJsonRpcEndpoint, IMcpClient
|
||||||
if (options.Capabilities?.Sampling != null)
|
if (options.Capabilities?.Sampling != null)
|
||||||
{
|
{
|
||||||
SetRequestHandler<CreateMessageRequestParams, CreateMessageResult>("sampling/createMessage",
|
SetRequestHandler<CreateMessageRequestParams, CreateMessageResult>("sampling/createMessage",
|
||||||
async (request) => {
|
async (request) =>
|
||||||
|
{
|
||||||
if (SamplingHandler == null)
|
if (SamplingHandler == null)
|
||||||
{
|
{
|
||||||
// Setting the capability, but not a handler means we have nothing to return to the server
|
// Setting the capability, but not a handler means we have nothing to return to the server
|
||||||
|
|
@ -168,7 +169,7 @@ internal class McpClient : McpJsonRpcEndpoint, IMcpClient
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public async Task PingAsync(CancellationToken cancellationToken)
|
public async Task PingAsync(CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
_logger.PingingServer(EndpointName);
|
_logger.PingingServer(EndpointName);
|
||||||
await SendRequestAsync<dynamic>(
|
await SendRequestAsync<dynamic>(
|
||||||
|
|
@ -268,7 +269,7 @@ internal class McpClient : McpJsonRpcEndpoint, IMcpClient
|
||||||
}
|
}
|
||||||
if (argumentValue is null)
|
if (argumentValue is null)
|
||||||
{
|
{
|
||||||
_logger.InvalidCompletionArgumentValue(EndpointName, argumentValue);
|
_logger.InvalidCompletionArgumentValue(EndpointName, "null", argumentName);
|
||||||
throw new McpClientException("Argument value cannot be null");
|
throw new McpClientException("Argument value cannot be null");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -330,13 +331,13 @@ internal class McpClient : McpJsonRpcEndpoint, IMcpClient
|
||||||
new JsonRpcRequest
|
new JsonRpcRequest
|
||||||
{
|
{
|
||||||
Method = "tools/call",
|
Method = "tools/call",
|
||||||
Params = CreateParametersDictionary(toolName, arguments ?? new())
|
Params = CreateParametersDictionary(toolName, arguments ?? [])
|
||||||
},
|
},
|
||||||
cancellationToken
|
cancellationToken
|
||||||
).ConfigureAwait(false);
|
).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Dictionary<string, object?> CreateParametersDictionary(string nameParameter, Dictionary<string, object> arguments)
|
private static Dictionary<string, object?> CreateParametersDictionary(string nameParameter, Dictionary<string, object>? arguments)
|
||||||
{
|
{
|
||||||
var parameters = new Dictionary<string, object?>
|
var parameters = new Dictionary<string, object?>
|
||||||
{
|
{
|
||||||
|
|
@ -352,10 +353,10 @@ internal class McpClient : McpJsonRpcEndpoint, IMcpClient
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public Func<CreateMessageRequestParams, CancellationToken, Task<CreateMessageResult>>? SamplingHandler { get; set; }
|
public Func<CreateMessageRequestParams?, CancellationToken, Task<CreateMessageResult>>? SamplingHandler { get; set; }
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public Func<ListRootsRequestParams, CancellationToken, Task<ListRootsResult>>? RootsHandler { get; set; }
|
public Func<ListRootsRequestParams?, CancellationToken, Task<ListRootsResult>>? RootsHandler { get; set; }
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public override string EndpointName
|
public override string EndpointName
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ public class McpClientFactory
|
||||||
{
|
{
|
||||||
private readonly Dictionary<string, McpServerConfig> _serverConfigs;
|
private readonly Dictionary<string, McpServerConfig> _serverConfigs;
|
||||||
private readonly McpClientOptions _clientOptions;
|
private readonly McpClientOptions _clientOptions;
|
||||||
private readonly Dictionary<string, IMcpClient> _clients = new();
|
private readonly Dictionary<string, IMcpClient> _clients = [];
|
||||||
private readonly Func<McpServerConfig, IClientTransport> _transportFactoryMethod;
|
private readonly Func<McpServerConfig, IClientTransport> _transportFactoryMethod;
|
||||||
private readonly Func<IClientTransport, McpServerConfig, McpClientOptions, IMcpClient> _clientFactoryMethod;
|
private readonly Func<IClientTransport, McpServerConfig, McpClientOptions, IMcpClient> _clientFactoryMethod;
|
||||||
private readonly ILoggerFactory _loggerFactory;
|
private readonly ILoggerFactory _loggerFactory;
|
||||||
|
|
@ -47,12 +47,9 @@ public class McpClientFactory
|
||||||
// Initialize commands for stdio transport, this is to run commands in a shell even if specified directly, as otherwise
|
// Initialize commands for stdio transport, this is to run commands in a shell even if specified directly, as otherwise
|
||||||
// the stdio protocol will not work correctly.
|
// the stdio protocol will not work correctly.
|
||||||
_logger.InitializingStdioCommands();
|
_logger.InitializingStdioCommands();
|
||||||
foreach (var config in _serverConfigs.Values)
|
foreach (var config in _serverConfigs.Values.Where(c => c.TransportType.Equals(TransportTypes.StdIo, StringComparison.OrdinalIgnoreCase)))
|
||||||
{
|
{
|
||||||
if (config.TransportType.ToLowerInvariant() == "stdio")
|
InitializeCommand(config);
|
||||||
{
|
|
||||||
InitializeCommand(config);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -97,11 +94,11 @@ public class McpClientFactory
|
||||||
{
|
{
|
||||||
string endpointName = $"Client ({config.Id}: {config.Name})";
|
string endpointName = $"Client ({config.Id}: {config.Name})";
|
||||||
|
|
||||||
var options = string.Join(", ", config.TransportOptions?.Select(kv => $"{kv.Key}={kv.Value}") ?? Enumerable.Empty<string>());
|
var options = string.Join(", ", config.TransportOptions?.Select(kv => $"{kv.Key}={kv.Value}") ?? []);
|
||||||
_logger.CreatingTransport(endpointName, config.TransportType, options);
|
_logger.CreatingTransport(endpointName, config.TransportType, options);
|
||||||
return config.TransportType.ToLowerInvariant() switch
|
return config.TransportType.ToLowerInvariant() switch
|
||||||
{
|
{
|
||||||
"stdio" => new StdioClientTransport(new StdioClientTransportOptions
|
TransportTypes.StdIo => new StdioClientTransport(new StdioClientTransportOptions
|
||||||
{
|
{
|
||||||
Command = GetCommand(config),
|
Command = GetCommand(config),
|
||||||
Arguments = config.TransportOptions?.GetValueOrDefault("arguments")?.Split(' '),
|
Arguments = config.TransportOptions?.GetValueOrDefault("arguments")?.Split(' '),
|
||||||
|
|
@ -110,7 +107,7 @@ public class McpClientFactory
|
||||||
.Where(kv => kv.Key.StartsWith("env:"))
|
.Where(kv => kv.Key.StartsWith("env:"))
|
||||||
.ToDictionary(kv => kv.Key.Substring(4), kv => kv.Value)
|
.ToDictionary(kv => kv.Key.Substring(4), kv => kv.Value)
|
||||||
}, config, _loggerFactory),
|
}, config, _loggerFactory),
|
||||||
"sse" or "http" => new SseClientTransport(
|
TransportTypes.Sse or "http" => new SseClientTransport(
|
||||||
new SseClientTransportOptions
|
new SseClientTransportOptions
|
||||||
{
|
{
|
||||||
ConnectionTimeout = TimeSpan.FromSeconds(ParseOrDefault(config.TransportOptions, "connectionTimeout", 30)),
|
ConnectionTimeout = TimeSpan.FromSeconds(ParseOrDefault(config.TransportOptions, "connectionTimeout", 30)),
|
||||||
|
|
@ -124,7 +121,7 @@ public class McpClientFactory
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int ParseOrDefault(IDictionary<string, string>? options, string key, int defaultValue)
|
private static int ParseOrDefault(Dictionary<string, string>? options, string key, int defaultValue)
|
||||||
{
|
{
|
||||||
if (options?.TryGetValue(key, out var value) ?? false)
|
if (options?.TryGetValue(key, out var value) ?? false)
|
||||||
{
|
{
|
||||||
|
|
@ -135,15 +132,14 @@ public class McpClientFactory
|
||||||
return defaultValue;
|
return defaultValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
private string? GetCommand(McpServerConfig config)
|
private static string GetCommand(McpServerConfig config)
|
||||||
{
|
{
|
||||||
if (config.TransportOptions == null ||
|
var command = config.TransportOptions?.GetValueOrDefault("command");
|
||||||
string.IsNullOrEmpty(config.TransportOptions.GetValueOrDefault("command")))
|
|
||||||
{
|
|
||||||
return config.Location!;
|
|
||||||
}
|
|
||||||
|
|
||||||
return OperatingSystem.IsWindows() ? "cmd.exe" : config.TransportOptions?.GetValueOrDefault("command");
|
if (string.IsNullOrEmpty(command))
|
||||||
|
return config.Location!;
|
||||||
|
|
||||||
|
return OperatingSystem.IsWindows() ? "cmd.exe" : command;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -162,7 +158,7 @@ public class McpClientFactory
|
||||||
}
|
}
|
||||||
|
|
||||||
// On Windows, we need to wrap non-shell commands with cmd.exe /c
|
// On Windows, we need to wrap non-shell commands with cmd.exe /c
|
||||||
if (command.ToLowerInvariant().Contains("cmd.exe"))
|
if (command.Contains("cmd.exe", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
_logger.SkippingShellWrapper(endpointName);
|
_logger.SkippingShellWrapper(endpointName);
|
||||||
return;
|
return;
|
||||||
|
|
@ -171,8 +167,8 @@ public class McpClientFactory
|
||||||
// If the command is not empty and does not contain cmd.exe, we need to inject /c {command} (usually npx or uvicorn)
|
// If the command is not empty and does not contain cmd.exe, we need to inject /c {command} (usually npx or uvicorn)
|
||||||
// This is because the stdio transport will not work correctly if the command is not run in a shell
|
// 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.TryGetValue("arguments", out var args)
|
||||||
? $"/c {command} {config.TransportOptions["arguments"]}"
|
? $"/c {command} {args}"
|
||||||
: $"/c {command}";
|
: $"/c {command}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -28,7 +28,7 @@ public static class McpServerBuilderExtensions
|
||||||
/// <param name="builder">The builder instance.</param>
|
/// <param name="builder">The builder instance.</param>
|
||||||
/// <param name="handler">The handler.</param>
|
/// <param name="handler">The handler.</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static IMcpServerBuilder WithListToolsHandler(this IMcpServerBuilder builder, Func<ListToolsRequestParams, CancellationToken, Task<ListToolsResult>> handler)
|
public static IMcpServerBuilder WithListToolsHandler(this IMcpServerBuilder builder, Func<ListToolsRequestParams?, CancellationToken, Task<ListToolsResult>> handler)
|
||||||
{
|
{
|
||||||
builder.Services.Configure<McpServerDelegates>(s => s.ListToolsHandler = handler);
|
builder.Services.Configure<McpServerDelegates>(s => s.ListToolsHandler = handler);
|
||||||
return builder;
|
return builder;
|
||||||
|
|
@ -40,7 +40,7 @@ public static class McpServerBuilderExtensions
|
||||||
/// <param name="builder">The builder instance.</param>
|
/// <param name="builder">The builder instance.</param>
|
||||||
/// <param name="handler">The handler.</param>
|
/// <param name="handler">The handler.</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static IMcpServerBuilder WithCallToolHandler(this IMcpServerBuilder builder, Func<CallToolRequestParams, CancellationToken, Task<CallToolResponse>> handler)
|
public static IMcpServerBuilder WithCallToolHandler(this IMcpServerBuilder builder, Func<CallToolRequestParams?, CancellationToken, Task<CallToolResponse>> handler)
|
||||||
{
|
{
|
||||||
builder.Services.Configure<McpServerDelegates>(s => s.CallToolHandler = handler);
|
builder.Services.Configure<McpServerDelegates>(s => s.CallToolHandler = handler);
|
||||||
return builder;
|
return builder;
|
||||||
|
|
@ -52,7 +52,7 @@ public static class McpServerBuilderExtensions
|
||||||
/// <param name="builder">The builder instance.</param>
|
/// <param name="builder">The builder instance.</param>
|
||||||
/// <param name="handler">The handler.</param>
|
/// <param name="handler">The handler.</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static IMcpServerBuilder WithListPromptsHandler(this IMcpServerBuilder builder, Func<ListPromptsRequestParams, CancellationToken, Task<ListPromptsResult>> handler)
|
public static IMcpServerBuilder WithListPromptsHandler(this IMcpServerBuilder builder, Func<ListPromptsRequestParams?, CancellationToken, Task<ListPromptsResult>> handler)
|
||||||
{
|
{
|
||||||
builder.Services.Configure<McpServerDelegates>(s => s.ListPromptsHandler = handler);
|
builder.Services.Configure<McpServerDelegates>(s => s.ListPromptsHandler = handler);
|
||||||
return builder;
|
return builder;
|
||||||
|
|
@ -64,7 +64,7 @@ public static class McpServerBuilderExtensions
|
||||||
/// <param name="builder">The builder instance.</param>
|
/// <param name="builder">The builder instance.</param>
|
||||||
/// <param name="handler">The handler.</param>
|
/// <param name="handler">The handler.</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static IMcpServerBuilder WithGetPromptHandler(this IMcpServerBuilder builder, Func<GetPromptRequestParams, CancellationToken, Task<GetPromptResult>> handler)
|
public static IMcpServerBuilder WithGetPromptHandler(this IMcpServerBuilder builder, Func<GetPromptRequestParams?, CancellationToken, Task<GetPromptResult>> handler)
|
||||||
{
|
{
|
||||||
builder.Services.Configure<McpServerDelegates>(s => s.GetPromptHandler = handler);
|
builder.Services.Configure<McpServerDelegates>(s => s.GetPromptHandler = handler);
|
||||||
return builder;
|
return builder;
|
||||||
|
|
@ -76,7 +76,7 @@ public static class McpServerBuilderExtensions
|
||||||
/// <param name="builder">The builder instance.</param>
|
/// <param name="builder">The builder instance.</param>
|
||||||
/// <param name="handler">The handler.</param>
|
/// <param name="handler">The handler.</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static IMcpServerBuilder WithListResourcesHandler(this IMcpServerBuilder builder, Func<ListResourcesRequestParams, CancellationToken, Task<ListResourcesResult>> handler)
|
public static IMcpServerBuilder WithListResourcesHandler(this IMcpServerBuilder builder, Func<ListResourcesRequestParams?, CancellationToken, Task<ListResourcesResult>> handler)
|
||||||
{
|
{
|
||||||
builder.Services.Configure<McpServerDelegates>(s => s.ListResourcesHandler = handler);
|
builder.Services.Configure<McpServerDelegates>(s => s.ListResourcesHandler = handler);
|
||||||
return builder;
|
return builder;
|
||||||
|
|
@ -88,7 +88,7 @@ public static class McpServerBuilderExtensions
|
||||||
/// <param name="builder">The builder instance.</param>
|
/// <param name="builder">The builder instance.</param>
|
||||||
/// <param name="handler">The handler.</param>
|
/// <param name="handler">The handler.</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static IMcpServerBuilder WithReadResourceHandler(this IMcpServerBuilder builder, Func<ReadResourceRequestParams, CancellationToken, Task<ReadResourceResult>> handler)
|
public static IMcpServerBuilder WithReadResourceHandler(this IMcpServerBuilder builder, Func<ReadResourceRequestParams?, CancellationToken, Task<ReadResourceResult>> handler)
|
||||||
{
|
{
|
||||||
builder.Services.Configure<McpServerDelegates>(s => s.ReadResourceHandler = handler);
|
builder.Services.Configure<McpServerDelegates>(s => s.ReadResourceHandler = handler);
|
||||||
return builder;
|
return builder;
|
||||||
|
|
@ -100,7 +100,7 @@ public static class McpServerBuilderExtensions
|
||||||
/// <param name="builder">The builder instance.</param>
|
/// <param name="builder">The builder instance.</param>
|
||||||
/// <param name="handler">The handler.</param>
|
/// <param name="handler">The handler.</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static IMcpServerBuilder WithGetCompletionHandler(this IMcpServerBuilder builder, Func<CompleteRequestParams, CancellationToken, Task<CompleteResult>> handler)
|
public static IMcpServerBuilder WithGetCompletionHandler(this IMcpServerBuilder builder, Func<CompleteRequestParams?, CancellationToken, Task<CompleteResult>> handler)
|
||||||
{
|
{
|
||||||
builder.Services.Configure<McpServerDelegates>(s => s.GetCompletionHandler = handler);
|
builder.Services.Configure<McpServerDelegates>(s => s.GetCompletionHandler = handler);
|
||||||
return builder;
|
return builder;
|
||||||
|
|
|
||||||
|
|
@ -364,12 +364,13 @@ internal static partial class Log
|
||||||
[LoggerMessage(
|
[LoggerMessage(
|
||||||
EventId = 7006,
|
EventId = 7006,
|
||||||
Level = LogLevel.Trace,
|
Level = LogLevel.Trace,
|
||||||
Message = "Invalid completion argument value {argumentValue} for {endpointName}"
|
Message = "Invalid completion argument value {argumentValue} for {argumentName} for {endpointName}"
|
||||||
)]
|
)]
|
||||||
public static partial void InvalidCompletionArgumentValue(
|
public static partial void InvalidCompletionArgumentValue(
|
||||||
this ILogger logger,
|
this ILogger logger,
|
||||||
string endpointName,
|
string endpointName,
|
||||||
string argumentValue);
|
string argumentValue,
|
||||||
|
string argumentName);
|
||||||
|
|
||||||
[LoggerMessage(
|
[LoggerMessage(
|
||||||
EventId = 7007,
|
EventId = 7007,
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,11 @@
|
||||||
using System.Text.Json;
|
using System.Net.Http.Headers;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using McpDotNet.Protocol.Messages;
|
using System.Text.Json;
|
||||||
using McpDotNet.Configuration;
|
using McpDotNet.Configuration;
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using McpDotNet.Logging;
|
using McpDotNet.Logging;
|
||||||
using System.Net.Http.Headers;
|
using McpDotNet.Protocol.Messages;
|
||||||
using System.Text.Json.Serialization;
|
|
||||||
using McpDotNet.Utils.Json;
|
using McpDotNet.Utils.Json;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace McpDotNet.Protocol.Transport;
|
namespace McpDotNet.Protocol.Transport;
|
||||||
|
|
||||||
|
|
@ -24,7 +23,7 @@ public sealed class SseClientTransport : TransportBase, IClientTransport
|
||||||
private readonly ILogger<SseClientTransport> _logger;
|
private readonly ILogger<SseClientTransport> _logger;
|
||||||
private readonly McpServerConfig _serverConfig;
|
private readonly McpServerConfig _serverConfig;
|
||||||
private readonly JsonSerializerOptions _jsonOptions;
|
private readonly JsonSerializerOptions _jsonOptions;
|
||||||
private TaskCompletionSource<bool> _connectionEstablished;
|
private readonly TaskCompletionSource<bool> _connectionEstablished;
|
||||||
|
|
||||||
private string EndpointName => $"Client (SSE) for ({_serverConfig.Id}: {_serverConfig.Name})";
|
private string EndpointName => $"Client (SSE) for ({_serverConfig.Id}: {_serverConfig.Name})";
|
||||||
|
|
||||||
|
|
@ -100,7 +99,7 @@ public sealed class SseClientTransport : TransportBase, IClientTransport
|
||||||
);
|
);
|
||||||
|
|
||||||
string messageId = "(no id)";
|
string messageId = "(no id)";
|
||||||
RequestId? requestId = null;
|
|
||||||
if (message is IJsonRpcMessageWithId messageWithId)
|
if (message is IJsonRpcMessageWithId messageWithId)
|
||||||
{
|
{
|
||||||
messageId = messageWithId.Id.ToString();
|
messageId = messageWithId.Id.ToString();
|
||||||
|
|
@ -114,24 +113,22 @@ public sealed class SseClientTransport : TransportBase, IClientTransport
|
||||||
|
|
||||||
response.EnsureSuccessStatusCode();
|
response.EnsureSuccessStatusCode();
|
||||||
|
|
||||||
var responseContent = await response.Content.ReadAsStringAsync();
|
var responseContent = await response.Content.ReadAsStringAsync(cancellationToken);
|
||||||
|
|
||||||
// Check if the message was an initialize request
|
// Check if the message was an initialize request
|
||||||
if (message is JsonRpcRequest request && request.Method == "initialize")
|
if (message is JsonRpcRequest request && request.Method == "initialize")
|
||||||
{
|
{
|
||||||
// If the response is not a JSON-RPC response, it is an SSE message
|
// If the response is not a JSON-RPC response, it is an SSE message
|
||||||
if (responseContent.ToLowerInvariant() == "accepted")
|
if (responseContent.Equals("accepted", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
_logger.SSETransportPostAccepted(EndpointName, messageId);
|
_logger.SSETransportPostAccepted(EndpointName, messageId);
|
||||||
// The response will arrive as an SSE message
|
// The response will arrive as an SSE message
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
JsonRpcResponse initializeResponse = JsonSerializer.Deserialize<JsonRpcResponse>(responseContent, _jsonOptions);
|
JsonRpcResponse initializeResponse = JsonSerializer.Deserialize<JsonRpcResponse>(responseContent, _jsonOptions) ??
|
||||||
if (initializeResponse == null)
|
|
||||||
{
|
|
||||||
throw new McpTransportException("Failed to initialize client");
|
throw new McpTransportException("Failed to initialize client");
|
||||||
}
|
|
||||||
_logger.TransportReceivedMessageParsed(EndpointName, messageId);
|
_logger.TransportReceivedMessageParsed(EndpointName, messageId);
|
||||||
await WriteMessageAsync(initializeResponse, cancellationToken);
|
await WriteMessageAsync(initializeResponse, cancellationToken);
|
||||||
_logger.TransportMessageWritten(EndpointName, messageId);
|
_logger.TransportMessageWritten(EndpointName, messageId);
|
||||||
|
|
@ -140,7 +137,7 @@ public sealed class SseClientTransport : TransportBase, IClientTransport
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise, check if the response was accepted (the response will come as an SSE message)
|
// Otherwise, check if the response was accepted (the response will come as an SSE message)
|
||||||
if (responseContent.ToLowerInvariant() == "accepted")
|
if (responseContent.Equals("accepted", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
_logger.SSETransportPostAccepted(EndpointName, messageId);
|
_logger.SSETransportPostAccepted(EndpointName, messageId);
|
||||||
}
|
}
|
||||||
|
|
@ -154,7 +151,7 @@ public sealed class SseClientTransport : TransportBase, IClientTransport
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public async Task CloseAsync()
|
public async Task CloseAsync()
|
||||||
{
|
{
|
||||||
_connectionCts.Cancel();
|
await _connectionCts.CancelAsync();
|
||||||
if (_receiveTask != null)
|
if (_receiveTask != null)
|
||||||
await _receiveTask;
|
await _receiveTask;
|
||||||
|
|
||||||
|
|
@ -290,7 +287,7 @@ public sealed class SseClientTransport : TransportBase, IClientTransport
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if data is absolute URI
|
// Check if data is absolute URI
|
||||||
if (data.ToLowerInvariant().StartsWith("http://") || data.ToLowerInvariant().StartsWith("https://"))
|
if (data.StartsWith("http://", StringComparison.OrdinalIgnoreCase) || data.StartsWith("https://", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
// Since the endpoint is an absolute URI, we can use it directly
|
// Since the endpoint is an absolute URI, we can use it directly
|
||||||
_messageEndpoint = new Uri(data);
|
_messageEndpoint = new Uri(data);
|
||||||
|
|
@ -317,10 +314,4 @@ public sealed class SseClientTransport : TransportBase, IClientTransport
|
||||||
throw new McpTransportException("Failed to parse endpoint event", ex);
|
throw new McpTransportException("Failed to parse endpoint event", ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private record EndpointEventData
|
|
||||||
{
|
|
||||||
[JsonPropertyName("uri")]
|
|
||||||
public string? Uri { get; init; }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using McpDotNet.Configuration;
|
using McpDotNet.Configuration;
|
||||||
|
using McpDotNet.Logging;
|
||||||
using McpDotNet.Protocol.Messages;
|
using McpDotNet.Protocol.Messages;
|
||||||
using McpDotNet.Utils.Json;
|
using McpDotNet.Utils.Json;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using McpDotNet.Logging;
|
|
||||||
|
|
||||||
namespace McpDotNet.Protocol.Transport;
|
namespace McpDotNet.Protocol.Transport;
|
||||||
|
|
||||||
|
|
@ -139,7 +139,7 @@ public sealed class StdioClientTransport : TransportBase, IClientTransport
|
||||||
|
|
||||||
// Write the message followed by a newline
|
// Write the message followed by a newline
|
||||||
await _process!.StandardInput.WriteLineAsync(json.AsMemory(), cancellationToken);
|
await _process!.StandardInput.WriteLineAsync(json.AsMemory(), cancellationToken);
|
||||||
await _process.StandardInput.FlushAsync();
|
await _process.StandardInput.FlushAsync(cancellationToken);
|
||||||
|
|
||||||
_logger.TransportSentMessage(EndpointName, id);
|
_logger.TransportSentMessage(EndpointName, id);
|
||||||
}
|
}
|
||||||
|
|
@ -228,7 +228,7 @@ public sealed class StdioClientTransport : TransportBase, IClientTransport
|
||||||
{
|
{
|
||||||
_logger.TransportCleaningUp(EndpointName);
|
_logger.TransportCleaningUp(EndpointName);
|
||||||
if (_process != null && _processStarted && !_process.HasExited)
|
if (_process != null && _processStarted && !_process.HasExited)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Try to close stdin to signal the process to exit
|
// Try to close stdin to signal the process to exit
|
||||||
|
|
@ -253,7 +253,8 @@ public sealed class StdioClientTransport : TransportBase, IClientTransport
|
||||||
_process = null;
|
_process = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
_shutdownCts?.Cancel();
|
if (_shutdownCts != null)
|
||||||
|
await _shutdownCts.CancelAsync();
|
||||||
_shutdownCts?.Dispose();
|
_shutdownCts?.Dispose();
|
||||||
_shutdownCts = null;
|
_shutdownCts = null;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ public record StdioClientTransportOptions
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Arguments to pass to the server process.
|
/// Arguments to pass to the server process.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string[]? Arguments { get; set; } = Array.Empty<string>();
|
public string[]? Arguments { get; set; } = [];
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The working directory for the server process.
|
/// The working directory for the server process.
|
||||||
|
|
|
||||||
17
src/mcpdotnet/Protocol/Transport/TransportTypes.cs
Normal file
17
src/mcpdotnet/Protocol/Transport/TransportTypes.cs
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
namespace McpDotNet.Protocol.Transport;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// List all transport types
|
||||||
|
/// </summary>
|
||||||
|
public static class TransportTypes
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The name of the Standard IO transport.
|
||||||
|
/// </summary>
|
||||||
|
public const string StdIo = "stdio";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The name of the ServerSideEvents transport.
|
||||||
|
/// </summary>
|
||||||
|
public const string Sse = "sse";
|
||||||
|
}
|
||||||
|
|
@ -8,9 +8,15 @@ namespace McpDotNet.Protocol.Types;
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class Argument
|
public class Argument
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The name of the argument.
|
||||||
|
/// </summary>
|
||||||
[JsonPropertyName("name")]
|
[JsonPropertyName("name")]
|
||||||
public string Name { get; set; } = string.Empty;
|
public string Name { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The value of the argument to use for completion matching.
|
||||||
|
/// </summary>
|
||||||
[JsonPropertyName("value")]
|
[JsonPropertyName("value")]
|
||||||
public string Value { get; set; } = string.Empty;
|
public string Value { get; set; } = string.Empty;
|
||||||
}
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue