Enable .NET SDK code analyzers (#41)
This commit is contained in:
parent
9c597aad90
commit
587e05f057
48 changed files with 301 additions and 151 deletions
18
.editorconfig
Normal file
18
.editorconfig
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
# Remove the line below if you want to inherit .editorconfig settings from higher directories
|
||||||
|
root = true
|
||||||
|
|
||||||
|
# C# files
|
||||||
|
[*.cs]
|
||||||
|
|
||||||
|
# Code Analysis
|
||||||
|
dotnet_diagnostic.CA1002.severity = none # CA1002: Do not expose generic lists
|
||||||
|
dotnet_diagnostic.CA1031.severity = none # CA1031: Do not catch general exception types
|
||||||
|
dotnet_diagnostic.CA1054.severity = none # CA1054: URI-like parameters should not be strings
|
||||||
|
dotnet_diagnostic.CA1056.severity = none # CA1056: URI-like properties should not be strings
|
||||||
|
dotnet_diagnostic.CA1062.severity = suggestion # CA1062: Validate arguments of public methods
|
||||||
|
dotnet_diagnostic.CA1510.severity = suggestion # CA1510: Use ArgumentNullException throw helper
|
||||||
|
dotnet_diagnostic.CA1849.severity = suggestion # CA1849: Call async methods when in an async method
|
||||||
|
dotnet_diagnostic.CA2000.severity = suggestion # CA2000: Dispose objects before losing scope
|
||||||
|
dotnet_diagnostic.CA2227.severity = suggestion # CA2227: Collection properties should be read only
|
||||||
|
dotnet_diagnostic.CA2249.severity = suggestion # CA2249: Use 'string.Contains' instead of 'string.IndexOf'
|
||||||
|
dotnet_diagnostic.CA1819.severity = suggestion # CA1819: Properties should not return arrays
|
||||||
|
|
@ -5,7 +5,7 @@ namespace TestServerWithHosting.Tools;
|
||||||
[McpToolType]
|
[McpToolType]
|
||||||
public static class EchoTool
|
public static class EchoTool
|
||||||
{
|
{
|
||||||
[McpTool(Description = "Echoes the input back to the client.")]
|
[McpTool(description: "Echoes the input back to the client.")]
|
||||||
public static string Echo([McpParameter(true)] string message)
|
public static string Echo([McpParameter(true)] string message)
|
||||||
{
|
{
|
||||||
return "hello " + message;
|
return "hello " + message;
|
||||||
|
|
|
||||||
|
|
@ -16,8 +16,11 @@ public class SampleLlmTool
|
||||||
_server = server ?? throw new ArgumentNullException(nameof(server));
|
_server = server ?? throw new ArgumentNullException(nameof(server));
|
||||||
}
|
}
|
||||||
|
|
||||||
[McpTool(Name = "sampleLLM", Description = "Samples from an LLM using MCP's sampling feature")]
|
[McpTool(name: "sampleLLM", description: "Samples from an LLM using MCP's sampling feature")]
|
||||||
public async Task<string> SampleLLM([McpParameter(true, Description = "The prompt to send to the LLM")] string prompt, [McpParameter(true, Description = "Maximum number of tokens to generate")] int maxTokens, CancellationToken cancellationToken)
|
public async Task<string> SampleLLM(
|
||||||
|
[McpParameter(true, description: "The prompt to send to the LLM")] string prompt,
|
||||||
|
[McpParameter(true, description: "Maximum number of tokens to generate")] int maxTokens,
|
||||||
|
CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var samplingParams = CreateRequestSamplingParams(prompt ?? string.Empty, "sampleLLM", maxTokens);
|
var samplingParams = CreateRequestSamplingParams(prompt ?? string.Empty, "sampleLLM", maxTokens);
|
||||||
var sampleResult = await _server.RequestSamplingAsync(samplingParams, cancellationToken);
|
var sampleResult = await _server.RequestSamplingAsync(samplingParams, cancellationToken);
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,17 @@
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Text.Json.Nodes;
|
using System.Text.Json.Nodes;
|
||||||
|
|
||||||
|
namespace McpDotNet;
|
||||||
|
|
||||||
public static class McpToolExtensions
|
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)
|
||||||
{
|
{
|
||||||
|
if (tools is null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(tools));
|
||||||
|
}
|
||||||
|
|
||||||
List<Anthropic.SDK.Common.Tool> result = [];
|
List<Anthropic.SDK.Common.Tool> result = [];
|
||||||
foreach (var tool in tools)
|
foreach (var tool in tools)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
using Anthropic.SDK;
|
using Anthropic.SDK;
|
||||||
using Anthropic.SDK.Constants;
|
using Anthropic.SDK.Constants;
|
||||||
using Anthropic.SDK.Messaging;
|
using Anthropic.SDK.Messaging;
|
||||||
|
using McpDotNet;
|
||||||
using McpDotNet.Client;
|
using McpDotNet.Client;
|
||||||
using McpDotNet.Configuration;
|
using McpDotNet.Configuration;
|
||||||
using McpDotNet.Protocol.Transport;
|
using McpDotNet.Protocol.Transport;
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,11 @@ 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)
|
||||||
{
|
{
|
||||||
|
if (arguments is null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(arguments));
|
||||||
|
}
|
||||||
|
|
||||||
// Convert arguments to dictionary format expected by mcpdotnet
|
// Convert arguments to dictionary format expected by mcpdotnet
|
||||||
Dictionary<string, object> argDict = [];
|
Dictionary<string, object> argDict = [];
|
||||||
foreach (var arg in arguments)
|
foreach (var arg in arguments)
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,11 @@ public class McpAIFunction : AIFunction
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
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)
|
||||||
{
|
{
|
||||||
|
if (arguments is null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(arguments));
|
||||||
|
}
|
||||||
|
|
||||||
// Convert arguments to dictionary format expected by mcpdotnet
|
// Convert arguments to dictionary format expected by mcpdotnet
|
||||||
Dictionary<string, object> argDict = [];
|
Dictionary<string, object> argDict = [];
|
||||||
foreach (var arg in arguments)
|
foreach (var arg in arguments)
|
||||||
|
|
@ -45,7 +50,7 @@ public class McpAIFunction : AIFunction
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call the tool through mcpdotnet
|
// Call the tool through mcpdotnet
|
||||||
var result = await _client.CallToolAsync(_tool.Name, argDict, cancellationToken);
|
var result = await _client.CallToolAsync(_tool.Name, argDict, cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
// Extract the text content from the result.
|
// Extract the text content from the result.
|
||||||
return string.Join("\n", result.Content
|
return string.Join("\n", result.Content
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,8 @@
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<LangVersion>Latest</LangVersion>
|
<LangVersion>Latest</LangVersion>
|
||||||
|
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||||
|
<AnalysisMode>All</AnalysisMode>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ namespace McpDotNet.Extensions.AI;
|
||||||
///
|
///
|
||||||
/// Disposing the scope will dispose all clients and tools, and close all connections.
|
/// Disposing the scope will dispose all clients and tools, and close all connections.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class McpSessionScope : IAsyncDisposable
|
public sealed class McpSessionScope : IAsyncDisposable
|
||||||
{
|
{
|
||||||
private readonly List<IMcpClient> _clients = [];
|
private readonly List<IMcpClient> _clients = [];
|
||||||
private bool _disposed;
|
private bool _disposed;
|
||||||
|
|
@ -39,9 +39,14 @@ public class McpSessionScope : IAsyncDisposable
|
||||||
McpClientOptions? options = null,
|
McpClientOptions? options = null,
|
||||||
ILoggerFactory? loggerFactory = null)
|
ILoggerFactory? loggerFactory = null)
|
||||||
{
|
{
|
||||||
|
if (serverConfig is null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(serverConfig));
|
||||||
|
}
|
||||||
|
|
||||||
var scope = new McpSessionScope();
|
var scope = new McpSessionScope();
|
||||||
var client = await scope.AddClientAsync(serverConfig, options, loggerFactory);
|
var client = await scope.AddClientAsync(serverConfig, options, loggerFactory).ConfigureAwait(false);
|
||||||
var tools = await client.ListToolsAsync();
|
var tools = await client.ListToolsAsync().ConfigureAwait(false);
|
||||||
scope.Tools = tools.Tools.Select(t => new McpAIFunction(t, client)).ToList<AITool>();
|
scope.Tools = tools.Tools.Select(t => new McpAIFunction(t, client)).ToList<AITool>();
|
||||||
if (!string.IsNullOrEmpty(client.ServerInstructions))
|
if (!string.IsNullOrEmpty(client.ServerInstructions))
|
||||||
{
|
{
|
||||||
|
|
@ -61,11 +66,16 @@ public class McpSessionScope : IAsyncDisposable
|
||||||
McpClientOptions? options = null,
|
McpClientOptions? options = null,
|
||||||
ILoggerFactory? loggerFactory = null)
|
ILoggerFactory? loggerFactory = null)
|
||||||
{
|
{
|
||||||
|
if (serverConfigs is null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(serverConfigs));
|
||||||
|
}
|
||||||
|
|
||||||
var scope = new McpSessionScope();
|
var scope = new McpSessionScope();
|
||||||
foreach (var config in serverConfigs)
|
foreach (var config in serverConfigs)
|
||||||
{
|
{
|
||||||
var client = await scope.AddClientAsync(config, options, loggerFactory);
|
var client = await scope.AddClientAsync(config, options, loggerFactory).ConfigureAwait(false);
|
||||||
var tools = await client.ListToolsAsync();
|
var tools = await client.ListToolsAsync().ConfigureAwait(false);
|
||||||
scope.Tools = scope.Tools.Concat(tools.Tools.Select(t => new McpAIFunction(t, client))).ToList();
|
scope.Tools = scope.Tools.Concat(tools.Tools.Select(t => new McpAIFunction(t, client))).ToList();
|
||||||
}
|
}
|
||||||
scope.ServerInstructions = scope._clients.Select(c => c.ServerInstructions).Where(s => !string.IsNullOrEmpty(s)).ToList()!;
|
scope.ServerInstructions = scope._clients.Select(c => c.ServerInstructions).Where(s => !string.IsNullOrEmpty(s)).ToList()!;
|
||||||
|
|
@ -79,7 +89,7 @@ public class McpSessionScope : IAsyncDisposable
|
||||||
var factory = new McpClientFactory([config],
|
var factory = new McpClientFactory([config],
|
||||||
options ?? new() { ClientInfo = new() { Name = "AnonymousClient", Version = "1.0.0.0" } },
|
options ?? new() { ClientInfo = new() { Name = "AnonymousClient", Version = "1.0.0.0" } },
|
||||||
loggerFactory ?? NullLoggerFactory.Instance);
|
loggerFactory ?? NullLoggerFactory.Instance);
|
||||||
var client = await factory.GetClientAsync(config.Id);
|
var client = await factory.GetClientAsync(config.Id).ConfigureAwait(false);
|
||||||
_clients.Add(client);
|
_clients.Add(client);
|
||||||
return client;
|
return client;
|
||||||
}
|
}
|
||||||
|
|
@ -92,7 +102,7 @@ public class McpSessionScope : IAsyncDisposable
|
||||||
|
|
||||||
foreach (var client in _clients)
|
foreach (var client in _clients)
|
||||||
{
|
{
|
||||||
await client.DisposeAsync();
|
await client.DisposeAsync().ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
_clients.Clear();
|
_clients.Clear();
|
||||||
Tools = [];
|
Tools = [];
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
namespace McpDotNet.Client;
|
using McpDotNet.Protocol.Messages;
|
||||||
|
|
||||||
using McpDotNet.Protocol.Messages;
|
|
||||||
using McpDotNet.Protocol.Types;
|
using McpDotNet.Protocol.Types;
|
||||||
|
|
||||||
|
namespace McpDotNet.Client;
|
||||||
|
|
||||||
/// <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.
|
||||||
/// Use PingAsync, ListToolsAsync and CallToolAsync instead of SendRequestAsync for common operations.
|
/// Use PingAsync, ListToolsAsync and CallToolAsync instead of SendRequestAsync for common operations.
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,7 @@ internal class McpClient : McpJsonRpcEndpoint, IMcpClient
|
||||||
throw new McpClientException("Sampling handler not configured");
|
throw new McpClientException("Sampling handler not configured");
|
||||||
}
|
}
|
||||||
|
|
||||||
return await SamplingHandler(request, CancellationTokenSource?.Token ?? CancellationToken.None);
|
return await SamplingHandler(request, CancellationTokenSource?.Token ?? CancellationToken.None).ConfigureAwait(false);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (options.Capabilities?.Roots != null)
|
if (options.Capabilities?.Roots != null)
|
||||||
|
|
@ -69,7 +69,7 @@ internal class McpClient : McpJsonRpcEndpoint, IMcpClient
|
||||||
_logger.RootsHandlerNotConfigured(EndpointName);
|
_logger.RootsHandlerNotConfigured(EndpointName);
|
||||||
throw new McpClientException("Roots handler not configured");
|
throw new McpClientException("Roots handler not configured");
|
||||||
}
|
}
|
||||||
return await RootsHandler(request, CancellationTokenSource?.Token ?? CancellationToken.None);
|
return await RootsHandler(request, CancellationTokenSource?.Token ?? CancellationToken.None).ConfigureAwait(false);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,13 @@ public class McpClientException : Exception
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int? ErrorCode { get; }
|
public int? ErrorCode { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="McpClientException"/> class.
|
||||||
|
/// </summary>
|
||||||
|
public McpClientException()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="McpClientException"/> class with a specified error message.
|
/// Initializes a new instance of the <see cref="McpClientException"/> class with a specified error message.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -81,7 +81,7 @@ public class McpClientFactory
|
||||||
|
|
||||||
var transport = _transportFactoryMethod(config);
|
var transport = _transportFactoryMethod(config);
|
||||||
var client = _clientFactoryMethod(transport, config, _clientOptions);
|
var client = _clientFactoryMethod(transport, config, _clientOptions);
|
||||||
await client.ConnectAsync(cancellationToken);
|
await client.ConnectAsync(cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
_logger.ClientCreated(endpointName);
|
_logger.ClientCreated(endpointName);
|
||||||
|
|
||||||
|
|
@ -97,29 +97,36 @@ public class McpClientFactory
|
||||||
|
|
||||||
var options = string.Join(", ", config.TransportOptions?.Select(kv => $"{kv.Key}={kv.Value}") ?? []);
|
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
|
|
||||||
|
if (string.Equals(config.TransportType, TransportTypes.StdIo, StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
TransportTypes.StdIo => new StdioClientTransport(new StdioClientTransportOptions
|
return new StdioClientTransport(new StdioClientTransportOptions
|
||||||
{
|
{
|
||||||
Command = GetCommand(config),
|
Command = GetCommand(config),
|
||||||
Arguments = config.TransportOptions?.GetValueOrDefault("arguments")?.Split(' '),
|
Arguments = config.TransportOptions?.GetValueOrDefault("arguments")?.Split(' '),
|
||||||
WorkingDirectory = config.TransportOptions?.GetValueOrDefault("workingDirectory"),
|
WorkingDirectory = config.TransportOptions?.GetValueOrDefault("workingDirectory"),
|
||||||
EnvironmentVariables = config.TransportOptions?
|
EnvironmentVariables = config.TransportOptions?
|
||||||
.Where(kv => kv.Key.StartsWith("env:"))
|
.Where(kv => kv.Key.StartsWith("env:", StringComparison.Ordinal))
|
||||||
.ToDictionary(kv => kv.Key.Substring(4), kv => kv.Value)
|
.ToDictionary(kv => kv.Key.Substring(4), kv => kv.Value)
|
||||||
}, config, _loggerFactory),
|
}, config, _loggerFactory);
|
||||||
TransportTypes.Sse or "http" => new SseClientTransport(
|
}
|
||||||
|
|
||||||
|
if (string.Equals(config.TransportType, TransportTypes.Sse, StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
string.Equals(config.TransportType, "http", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
return new SseClientTransport(
|
||||||
new SseClientTransportOptions
|
new SseClientTransportOptions
|
||||||
{
|
{
|
||||||
ConnectionTimeout = TimeSpan.FromSeconds(ParseOrDefault(config.TransportOptions, "connectionTimeout", 30)),
|
ConnectionTimeout = TimeSpan.FromSeconds(ParseOrDefault(config.TransportOptions, "connectionTimeout", 30)),
|
||||||
MaxReconnectAttempts = ParseOrDefault(config.TransportOptions, "maxReconnectAttempts", 3),
|
MaxReconnectAttempts = ParseOrDefault(config.TransportOptions, "maxReconnectAttempts", 3),
|
||||||
ReconnectDelay = TimeSpan.FromSeconds(ParseOrDefault(config.TransportOptions, "reconnectDelay", 5)),
|
ReconnectDelay = TimeSpan.FromSeconds(ParseOrDefault(config.TransportOptions, "reconnectDelay", 5)),
|
||||||
AdditionalHeaders = config.TransportOptions?
|
AdditionalHeaders = config.TransportOptions?
|
||||||
.Where(kv => kv.Key.StartsWith("header."))
|
.Where(kv => kv.Key.StartsWith("header.", StringComparison.Ordinal))
|
||||||
.ToDictionary(kv => kv.Key.Substring(7), kv => kv.Value)
|
.ToDictionary(kv => kv.Key.Substring(7), kv => kv.Value)
|
||||||
}, config, _loggerFactory),
|
}, config, _loggerFactory);
|
||||||
_ => throw new ArgumentException($"Unsupported transport type '{config.TransportType}'.", nameof(config))
|
}
|
||||||
};
|
|
||||||
|
throw new ArgumentException($"Unsupported transport type '{config.TransportType}'.", nameof(config));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int ParseOrDefault(Dictionary<string, string>? options, string key, int defaultValue)
|
private static int ParseOrDefault(Dictionary<string, string>? options, string key, int defaultValue)
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,11 @@ public static partial class McpServerBuilderExtensions
|
||||||
/// <param name="handler">The handler.</param>
|
/// <param name="handler">The handler.</param>
|
||||||
public static IMcpServerBuilder WithListToolsHandler(this IMcpServerBuilder builder, Func<RequestContext<ListToolsRequestParams>, CancellationToken, Task<ListToolsResult>> handler)
|
public static IMcpServerBuilder WithListToolsHandler(this IMcpServerBuilder builder, Func<RequestContext<ListToolsRequestParams>, CancellationToken, Task<ListToolsResult>> handler)
|
||||||
{
|
{
|
||||||
|
if (builder is null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(builder));
|
||||||
|
}
|
||||||
|
|
||||||
builder.Services.Configure<McpServerDelegates>(s => s.ListToolsHandler = handler);
|
builder.Services.Configure<McpServerDelegates>(s => s.ListToolsHandler = handler);
|
||||||
return builder;
|
return builder;
|
||||||
}
|
}
|
||||||
|
|
@ -28,6 +33,11 @@ public static partial class McpServerBuilderExtensions
|
||||||
/// <param name="handler">The handler.</param>
|
/// <param name="handler">The handler.</param>
|
||||||
public static IMcpServerBuilder WithCallToolHandler(this IMcpServerBuilder builder, Func<RequestContext<CallToolRequestParams>, CancellationToken, Task<CallToolResponse>> handler)
|
public static IMcpServerBuilder WithCallToolHandler(this IMcpServerBuilder builder, Func<RequestContext<CallToolRequestParams>, CancellationToken, Task<CallToolResponse>> handler)
|
||||||
{
|
{
|
||||||
|
if (builder is null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(builder));
|
||||||
|
}
|
||||||
|
|
||||||
builder.Services.Configure<McpServerDelegates>(s => s.CallToolHandler = handler);
|
builder.Services.Configure<McpServerDelegates>(s => s.CallToolHandler = handler);
|
||||||
return builder;
|
return builder;
|
||||||
}
|
}
|
||||||
|
|
@ -39,6 +49,11 @@ public static partial class McpServerBuilderExtensions
|
||||||
/// <param name="handler">The handler.</param>
|
/// <param name="handler">The handler.</param>
|
||||||
public static IMcpServerBuilder WithListPromptsHandler(this IMcpServerBuilder builder, Func<RequestContext<ListPromptsRequestParams>, CancellationToken, Task<ListPromptsResult>> handler)
|
public static IMcpServerBuilder WithListPromptsHandler(this IMcpServerBuilder builder, Func<RequestContext<ListPromptsRequestParams>, CancellationToken, Task<ListPromptsResult>> handler)
|
||||||
{
|
{
|
||||||
|
if (builder is null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(builder));
|
||||||
|
}
|
||||||
|
|
||||||
builder.Services.Configure<McpServerDelegates>(s => s.ListPromptsHandler = handler);
|
builder.Services.Configure<McpServerDelegates>(s => s.ListPromptsHandler = handler);
|
||||||
return builder;
|
return builder;
|
||||||
}
|
}
|
||||||
|
|
@ -50,6 +65,11 @@ public static partial class McpServerBuilderExtensions
|
||||||
/// <param name="handler">The handler.</param>
|
/// <param name="handler">The handler.</param>
|
||||||
public static IMcpServerBuilder WithGetPromptHandler(this IMcpServerBuilder builder, Func<RequestContext<GetPromptRequestParams>, CancellationToken, Task<GetPromptResult>> handler)
|
public static IMcpServerBuilder WithGetPromptHandler(this IMcpServerBuilder builder, Func<RequestContext<GetPromptRequestParams>, CancellationToken, Task<GetPromptResult>> handler)
|
||||||
{
|
{
|
||||||
|
if (builder is null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(builder));
|
||||||
|
}
|
||||||
|
|
||||||
builder.Services.Configure<McpServerDelegates>(s => s.GetPromptHandler = handler);
|
builder.Services.Configure<McpServerDelegates>(s => s.GetPromptHandler = handler);
|
||||||
return builder;
|
return builder;
|
||||||
}
|
}
|
||||||
|
|
@ -61,6 +81,11 @@ public static partial class McpServerBuilderExtensions
|
||||||
/// <param name="handler">The handler.</param>
|
/// <param name="handler">The handler.</param>
|
||||||
public static IMcpServerBuilder WithListResourcesHandler(this IMcpServerBuilder builder, Func<RequestContext<ListResourcesRequestParams>, CancellationToken, Task<ListResourcesResult>> handler)
|
public static IMcpServerBuilder WithListResourcesHandler(this IMcpServerBuilder builder, Func<RequestContext<ListResourcesRequestParams>, CancellationToken, Task<ListResourcesResult>> handler)
|
||||||
{
|
{
|
||||||
|
if (builder is null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(builder));
|
||||||
|
}
|
||||||
|
|
||||||
builder.Services.Configure<McpServerDelegates>(s => s.ListResourcesHandler = handler);
|
builder.Services.Configure<McpServerDelegates>(s => s.ListResourcesHandler = handler);
|
||||||
return builder;
|
return builder;
|
||||||
}
|
}
|
||||||
|
|
@ -72,6 +97,11 @@ public static partial class McpServerBuilderExtensions
|
||||||
/// <param name="handler">The handler.</param>
|
/// <param name="handler">The handler.</param>
|
||||||
public static IMcpServerBuilder WithReadResourceHandler(this IMcpServerBuilder builder, Func<RequestContext<ReadResourceRequestParams>, CancellationToken, Task<ReadResourceResult>> handler)
|
public static IMcpServerBuilder WithReadResourceHandler(this IMcpServerBuilder builder, Func<RequestContext<ReadResourceRequestParams>, CancellationToken, Task<ReadResourceResult>> handler)
|
||||||
{
|
{
|
||||||
|
if (builder is null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(builder));
|
||||||
|
}
|
||||||
|
|
||||||
builder.Services.Configure<McpServerDelegates>(s => s.ReadResourceHandler = handler);
|
builder.Services.Configure<McpServerDelegates>(s => s.ReadResourceHandler = handler);
|
||||||
return builder;
|
return builder;
|
||||||
}
|
}
|
||||||
|
|
@ -83,6 +113,11 @@ public static partial class McpServerBuilderExtensions
|
||||||
/// <param name="handler">The handler.</param>
|
/// <param name="handler">The handler.</param>
|
||||||
public static IMcpServerBuilder WithGetCompletionHandler(this IMcpServerBuilder builder, Func<RequestContext<CompleteRequestParams>, CancellationToken, Task<CompleteResult>> handler)
|
public static IMcpServerBuilder WithGetCompletionHandler(this IMcpServerBuilder builder, Func<RequestContext<CompleteRequestParams>, CancellationToken, Task<CompleteResult>> handler)
|
||||||
{
|
{
|
||||||
|
if (builder is null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(builder));
|
||||||
|
}
|
||||||
|
|
||||||
builder.Services.Configure<McpServerDelegates>(s => s.GetCompletionHandler = handler);
|
builder.Services.Configure<McpServerDelegates>(s => s.GetCompletionHandler = handler);
|
||||||
return builder;
|
return builder;
|
||||||
}
|
}
|
||||||
|
|
@ -94,6 +129,11 @@ public static partial class McpServerBuilderExtensions
|
||||||
/// <param name="handler">The handler.</param>
|
/// <param name="handler">The handler.</param>
|
||||||
public static IMcpServerBuilder WithSubscribeToResourcesHandler(this IMcpServerBuilder builder, Func<RequestContext<string>, CancellationToken, Task> handler)
|
public static IMcpServerBuilder WithSubscribeToResourcesHandler(this IMcpServerBuilder builder, Func<RequestContext<string>, CancellationToken, Task> handler)
|
||||||
{
|
{
|
||||||
|
if (builder is null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(builder));
|
||||||
|
}
|
||||||
|
|
||||||
builder.Services.Configure<McpServerDelegates>(s => s.SubscribeToResourcesHandler = handler);
|
builder.Services.Configure<McpServerDelegates>(s => s.SubscribeToResourcesHandler = handler);
|
||||||
return builder;
|
return builder;
|
||||||
}
|
}
|
||||||
|
|
@ -105,6 +145,11 @@ public static partial class McpServerBuilderExtensions
|
||||||
/// <param name="handler">The handler.</param>
|
/// <param name="handler">The handler.</param>
|
||||||
public static IMcpServerBuilder WithUnsubscribeFromResourcesHandler(this IMcpServerBuilder builder, Func<RequestContext<string>, CancellationToken, Task> handler)
|
public static IMcpServerBuilder WithUnsubscribeFromResourcesHandler(this IMcpServerBuilder builder, Func<RequestContext<string>, CancellationToken, Task> handler)
|
||||||
{
|
{
|
||||||
|
if (builder is null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(builder));
|
||||||
|
}
|
||||||
|
|
||||||
builder.Services.Configure<McpServerDelegates>(s => s.UnsubscribeFromResourcesHandler = handler);
|
builder.Services.Configure<McpServerDelegates>(s => s.UnsubscribeFromResourcesHandler = handler);
|
||||||
return builder;
|
return builder;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
using System.Reflection;
|
using System.Globalization;
|
||||||
|
using System.Reflection;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using McpDotNet.Configuration;
|
using McpDotNet.Configuration;
|
||||||
using McpDotNet.Protocol.Types;
|
using McpDotNet.Protocol.Types;
|
||||||
|
|
@ -37,6 +38,11 @@ public static partial class McpServerBuilderExtensions
|
||||||
/// <param name="toolTypes">Types with marked methods to add as tools to the server.</param>
|
/// <param name="toolTypes">Types with marked methods to add as tools to the server.</param>
|
||||||
public static IMcpServerBuilder WithTools(this IMcpServerBuilder builder, params Type[] toolTypes)
|
public static IMcpServerBuilder WithTools(this IMcpServerBuilder builder, params Type[] toolTypes)
|
||||||
{
|
{
|
||||||
|
if (builder is null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(builder));
|
||||||
|
}
|
||||||
|
|
||||||
if (toolTypes is null || toolTypes.Length == 0)
|
if (toolTypes is null || toolTypes.Length == 0)
|
||||||
throw new ArgumentException("At least one tool type must be provided.", nameof(toolTypes));
|
throw new ArgumentException("At least one tool type must be provided.", nameof(toolTypes));
|
||||||
|
|
||||||
|
|
@ -54,7 +60,7 @@ public static partial class McpServerBuilderExtensions
|
||||||
var tool = CreateTool(method, attribute);
|
var tool = CreateTool(method, attribute);
|
||||||
tools.Add(tool);
|
tools.Add(tool);
|
||||||
|
|
||||||
callbacks.Add(tool.Name, async (request, cancellationToken) => await CallTool(request, method, cancellationToken));
|
callbacks.Add(tool.Name, async (request, cancellationToken) => await CallTool(request, method, cancellationToken).ConfigureAwait(false));
|
||||||
|
|
||||||
// register type because method is not static and so we need an instance
|
// register type because method is not static and so we need an instance
|
||||||
if (!method.IsStatic)
|
if (!method.IsStatic)
|
||||||
|
|
@ -68,7 +74,7 @@ public static partial class McpServerBuilderExtensions
|
||||||
builder.WithCallToolHandler(async (request, cancellationToken) =>
|
builder.WithCallToolHandler(async (request, cancellationToken) =>
|
||||||
{
|
{
|
||||||
if (request.Params != null && callbacks.TryGetValue(request.Params.Name, out var callback))
|
if (request.Params != null && callbacks.TryGetValue(request.Params.Name, out var callback))
|
||||||
return await callback(request, cancellationToken);
|
return await callback(request, cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
throw new McpServerException($"Unknown tool: {request.Params?.Name}");
|
throw new McpServerException($"Unknown tool: {request.Params?.Name}");
|
||||||
});
|
});
|
||||||
|
|
@ -219,7 +225,7 @@ public static partial class McpServerBuilderExtensions
|
||||||
if (value is JsonElement element)
|
if (value is JsonElement element)
|
||||||
value = JsonSerializer.Deserialize(element.GetRawText(), parameter.ParameterType);
|
value = JsonSerializer.Deserialize(element.GetRawText(), parameter.ParameterType);
|
||||||
|
|
||||||
parameters.Add(Convert.ChangeType(value, parameter.ParameterType));
|
parameters.Add(Convert.ChangeType(value, parameter.ParameterType, CultureInfo.InvariantCulture));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,11 @@ public static partial class McpServerBuilderExtensions
|
||||||
/// <param name="builder">The builder instance.</param>
|
/// <param name="builder">The builder instance.</param>
|
||||||
public static IMcpServerBuilder WithStdioServerTransport(this IMcpServerBuilder builder)
|
public static IMcpServerBuilder WithStdioServerTransport(this IMcpServerBuilder builder)
|
||||||
{
|
{
|
||||||
|
if (builder is null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(builder));
|
||||||
|
}
|
||||||
|
|
||||||
builder.Services.AddSingleton<IServerTransport, StdioServerTransport>();
|
builder.Services.AddSingleton<IServerTransport, StdioServerTransport>();
|
||||||
return builder;
|
return builder;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,6 @@ public class McpServerHostedService : BackgroundService
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||||
{
|
{
|
||||||
await _server.StartAsync(stoppingToken);
|
await _server.StartAsync(stoppingToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
namespace McpDotNet.Protocol.Messages;
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
using System.Text.Json.Serialization;
|
namespace McpDotNet.Protocol.Messages;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// An error response message in the JSON-RPC protocol.
|
/// An error response message in the JSON-RPC protocol.
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
namespace McpDotNet.Protocol.Messages;
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
using System.Text.Json.Serialization;
|
namespace McpDotNet.Protocol.Messages;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Detailed error information for JSON-RPC error responses.
|
/// Detailed error information for JSON-RPC error responses.
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
namespace McpDotNet.Protocol.Messages;
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
using System.Text.Json.Serialization;
|
namespace McpDotNet.Protocol.Messages;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A request message in the JSON-RPC protocol.
|
/// A request message in the JSON-RPC protocol.
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue