Add net10.0 assets and run some fixers (#875)
This commit is contained in:
parent
e7b403b820
commit
bfc0980ef5
24 changed files with 168 additions and 194 deletions
|
|
@ -128,10 +128,7 @@ public static class AIContentExtensions
|
|||
_ => null,
|
||||
};
|
||||
|
||||
if (ac is not null)
|
||||
{
|
||||
ac.RawRepresentation = content;
|
||||
}
|
||||
ac?.RawRepresentation = content;
|
||||
|
||||
return ac;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
#if NET9_0_OR_GREATER
|
||||
using System.Buffers.Text;
|
||||
#endif
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Security.Cryptography;
|
||||
|
|
@ -238,14 +241,17 @@ internal sealed partial class ClientOAuthProvider
|
|||
LogOAuthAuthorizationCompleted();
|
||||
}
|
||||
|
||||
private static readonly string[] s_wellKnownPaths = [".well-known/openid-configuration", ".well-known/oauth-authorization-server"];
|
||||
|
||||
private async Task<AuthorizationServerMetadata> GetAuthServerMetadataAsync(Uri authServerUri, CancellationToken cancellationToken)
|
||||
{
|
||||
if (!authServerUri.OriginalString.EndsWith("/"))
|
||||
if (authServerUri.OriginalString.Length == 0 ||
|
||||
authServerUri.OriginalString[authServerUri.OriginalString.Length - 1] != '/')
|
||||
{
|
||||
authServerUri = new Uri(authServerUri.OriginalString + "/");
|
||||
authServerUri = new Uri($"{authServerUri.OriginalString}/");
|
||||
}
|
||||
|
||||
foreach (var path in new[] { ".well-known/openid-configuration", ".well-known/oauth-authorization-server" })
|
||||
foreach (var path in s_wellKnownPaths)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
|
@ -540,11 +546,7 @@ internal sealed partial class ClientOAuthProvider
|
|||
Port = -1 // Always remove port
|
||||
};
|
||||
|
||||
if (builder.Path == "/")
|
||||
{
|
||||
builder.Path = string.Empty;
|
||||
}
|
||||
else if (builder.Path.Length > 1 && builder.Path.EndsWith("/"))
|
||||
if (builder.Path.Length > 0 && builder.Path[builder.Path.Length - 1] == '/')
|
||||
{
|
||||
builder.Path = builder.Path.TrimEnd('/');
|
||||
}
|
||||
|
|
@ -633,18 +635,18 @@ internal sealed partial class ClientOAuthProvider
|
|||
continue;
|
||||
}
|
||||
|
||||
string key = trimmedPart.Substring(0, equalsIndex).Trim();
|
||||
ReadOnlySpan<char> key = trimmedPart.AsSpan().Slice(0, equalsIndex).Trim();
|
||||
|
||||
if (string.Equals(key, parameterName, StringComparison.OrdinalIgnoreCase))
|
||||
if (key.Equals(parameterName, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
string value = trimmedPart.Substring(equalsIndex + 1).Trim();
|
||||
ReadOnlySpan<char> value = trimmedPart.AsSpan(equalsIndex + 1).Trim();
|
||||
|
||||
if (value.StartsWith("\"") && value.EndsWith("\""))
|
||||
if (value.Length > 0 && value[0] == '"' && value[value.Length - 1] == '"')
|
||||
{
|
||||
value = value.Substring(1, value.Length - 2);
|
||||
value = value.Slice(1, value.Length - 2);
|
||||
}
|
||||
|
||||
return value;
|
||||
return value.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -664,12 +666,18 @@ internal sealed partial class ClientOAuthProvider
|
|||
|
||||
private static string GenerateCodeChallenge(string codeVerifier)
|
||||
{
|
||||
#if NET9_0_OR_GREATER
|
||||
Span<byte> hash = stackalloc byte[SHA256.HashSizeInBytes];
|
||||
SHA256.HashData(Encoding.UTF8.GetBytes(codeVerifier), hash);
|
||||
return Base64Url.EncodeToString(hash);
|
||||
#else
|
||||
using var sha256 = SHA256.Create();
|
||||
var challengeBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(codeVerifier));
|
||||
return Convert.ToBase64String(challengeBytes)
|
||||
.TrimEnd('=')
|
||||
.Replace('+', '-')
|
||||
.Replace('/', '_');
|
||||
#endif
|
||||
}
|
||||
|
||||
private string GetClientIdOrThrow() => _clientId ?? throw new InvalidOperationException("Client ID is not available. This may indicate an issue with dynamic client registration.");
|
||||
|
|
|
|||
|
|
@ -11,8 +11,6 @@ namespace ModelContextProtocol.Client;
|
|||
/// </remarks>
|
||||
public sealed class McpClientOptions
|
||||
{
|
||||
private McpClientHandlers? _handlers;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets information about this client implementation, including its name and version.
|
||||
/// </summary>
|
||||
|
|
@ -71,11 +69,11 @@ public sealed class McpClientOptions
|
|||
/// </summary>
|
||||
public McpClientHandlers Handlers
|
||||
{
|
||||
get => _handlers ??= new();
|
||||
get => field ??= new();
|
||||
set
|
||||
{
|
||||
Throw.IfNull(value);
|
||||
_handlers = value;
|
||||
field = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,21 +5,15 @@ using System.Diagnostics;
|
|||
namespace ModelContextProtocol.Client;
|
||||
|
||||
/// <summary>Provides the client side of a stdio-based session transport.</summary>
|
||||
internal sealed class StdioClientSessionTransport : StreamClientSessionTransport
|
||||
internal sealed class StdioClientSessionTransport(
|
||||
StdioClientTransportOptions options, Process process, string endpointName, Queue<string> stderrRollingLog, ILoggerFactory? loggerFactory) :
|
||||
StreamClientSessionTransport(process.StandardInput.BaseStream, process.StandardOutput.BaseStream, encoding: null, endpointName, loggerFactory)
|
||||
{
|
||||
private readonly StdioClientTransportOptions _options;
|
||||
private readonly Process _process;
|
||||
private readonly Queue<string> _stderrRollingLog;
|
||||
private readonly StdioClientTransportOptions _options = options;
|
||||
private readonly Process _process = process;
|
||||
private readonly Queue<string> _stderrRollingLog = stderrRollingLog;
|
||||
private int _cleanedUp = 0;
|
||||
|
||||
public StdioClientSessionTransport(StdioClientTransportOptions options, Process process, string endpointName, Queue<string> stderrRollingLog, ILoggerFactory? loggerFactory)
|
||||
: base(process.StandardInput.BaseStream, process.StandardOutput.BaseStream, encoding: null, endpointName, loggerFactory)
|
||||
{
|
||||
_process = process;
|
||||
_options = options;
|
||||
_stderrRollingLog = stderrRollingLog;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override async Task SendMessageAsync(JsonRpcMessage message, CancellationToken cancellationToken = default)
|
||||
{
|
||||
|
|
@ -56,7 +50,7 @@ internal sealed class StdioClientSessionTransport : StreamClientSessionTransport
|
|||
// Now terminate the server process.
|
||||
try
|
||||
{
|
||||
StdioClientTransport.DisposeProcess(_process, processRunning: true, _options.ShutdownTimeout, Name);
|
||||
StdioClientTransport.DisposeProcess(_process, processRunning: true, shutdownTimeout: _options.ShutdownTimeout);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ public sealed partial class StdioClientTransport : IClientTransport
|
|||
|
||||
_options = options;
|
||||
_loggerFactory = loggerFactory;
|
||||
Name = options.Name ?? $"stdio-{Regex.Replace(Path.GetFileName(options.Command), @"[\s\.]+", "-")}";
|
||||
Name = options.Name ?? $"stdio-{WhitespaceAndPeriods().Replace(Path.GetFileName(options.Command), "-")}";
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
|
@ -189,7 +189,7 @@ public sealed partial class StdioClientTransport : IClientTransport
|
|||
|
||||
try
|
||||
{
|
||||
DisposeProcess(process, processStarted, _options.ShutdownTimeout, endpointName);
|
||||
DisposeProcess(process, processStarted, _options.ShutdownTimeout);
|
||||
}
|
||||
catch (Exception ex2)
|
||||
{
|
||||
|
|
@ -201,7 +201,7 @@ public sealed partial class StdioClientTransport : IClientTransport
|
|||
}
|
||||
|
||||
internal static void DisposeProcess(
|
||||
Process? process, bool processRunning, TimeSpan shutdownTimeout, string endpointName)
|
||||
Process? process, bool processRunning, TimeSpan shutdownTimeout)
|
||||
{
|
||||
if (process is not null)
|
||||
{
|
||||
|
|
@ -279,4 +279,12 @@ public sealed partial class StdioClientTransport : IClientTransport
|
|||
|
||||
[LoggerMessage(Level = LogLevel.Warning, Message = "{EndpointName} shutdown failed.")]
|
||||
private static partial void LogTransportShutdownFailed(ILogger logger, string endpointName, Exception exception);
|
||||
|
||||
#if NET
|
||||
[GeneratedRegex(@"[\s\.]+")]
|
||||
private static partial Regex WhitespaceAndPeriods();
|
||||
#else
|
||||
private static Regex WhitespaceAndPeriods() => s_whitespaceAndPeriods;
|
||||
private static readonly Regex s_whitespaceAndPeriods = new(@"[\s\.]+", RegexOptions.Compiled);
|
||||
#endif
|
||||
}
|
||||
|
|
|
|||
|
|
@ -93,9 +93,9 @@ internal static class Diagnostics
|
|||
{
|
||||
if (jsonObject["_meta"] is not JsonObject meta)
|
||||
{
|
||||
meta = new JsonObject();
|
||||
jsonObject["_meta"] = meta;
|
||||
jsonObject["_meta"] = meta = [];
|
||||
}
|
||||
|
||||
meta[key] = value;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -405,7 +405,7 @@ internal sealed partial class McpSessionHandler : IAsyncDisposable
|
|||
|
||||
long? startingTimestamp = durationMetric.Enabled ? Stopwatch.GetTimestamp() : null;
|
||||
using Activity? activity = Diagnostics.ShouldInstrumentMessage(request) ?
|
||||
Diagnostics.ActivitySource.StartActivity(CreateActivityName(method), ActivityKind.Client) :
|
||||
Diagnostics.ActivitySource.StartActivity(McpSessionHandler.CreateActivityName(method), ActivityKind.Client) :
|
||||
null;
|
||||
|
||||
// Set request ID
|
||||
|
|
@ -501,7 +501,7 @@ internal sealed partial class McpSessionHandler : IAsyncDisposable
|
|||
|
||||
long? startingTimestamp = durationMetric.Enabled ? Stopwatch.GetTimestamp() : null;
|
||||
using Activity? activity = Diagnostics.ShouldInstrumentMessage(message) ?
|
||||
Diagnostics.ActivitySource.StartActivity(CreateActivityName(method), ActivityKind.Client) :
|
||||
Diagnostics.ActivitySource.StartActivity(McpSessionHandler.CreateActivityName(method), ActivityKind.Client) :
|
||||
null;
|
||||
|
||||
TagList tags = default;
|
||||
|
|
@ -567,7 +567,7 @@ internal sealed partial class McpSessionHandler : IAsyncDisposable
|
|||
}
|
||||
}
|
||||
|
||||
private string CreateActivityName(string method) => method;
|
||||
private static string CreateActivityName(string method) => method;
|
||||
|
||||
private static string GetMethodName(JsonRpcMessage message) =>
|
||||
message switch
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net9.0;net8.0;netstandard2.0</TargetFrameworks>
|
||||
<TargetFrameworks>net10.0;net9.0;net8.0;netstandard2.0</TargetFrameworks>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<IsPackable>true</IsPackable>
|
||||
<PackageId>ModelContextProtocol.Core</PackageId>
|
||||
|
|
@ -27,16 +27,20 @@
|
|||
<PackageReference Include="System.Threading.Channels" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Dependencies only needed by netstandard2.0 or net8.0 -->
|
||||
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0' or '$(TargetFramework)' == 'net8.0'">
|
||||
<!-- Dependencies only needed by pre-net9.0 -->
|
||||
<ItemGroup Condition="!$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', 'net9.0'))">
|
||||
<PackageReference Include="System.IO.Pipelines" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Dependencies only needed pre-net10.0 -->
|
||||
<ItemGroup Condition="!$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', 'net10.0'))">
|
||||
<PackageReference Include="System.Net.ServerSentEvents" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Dependencies needed by all -->
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.AI.Abstractions" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" />
|
||||
<PackageReference Include="System.Net.ServerSentEvents" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
|||
|
|
@ -240,14 +240,8 @@ internal sealed class NotificationHandlers
|
|||
// to point past this one. Importantly, we do not modify this node's Next or Prev.
|
||||
// We want to ensure that an enumeration through all of the registrations can still
|
||||
// progress through this one.
|
||||
if (Prev is not null)
|
||||
{
|
||||
Prev.Next = Next;
|
||||
}
|
||||
if (Next is not null)
|
||||
{
|
||||
Next.Prev = Prev;
|
||||
}
|
||||
Prev?.Next = Next;
|
||||
Next?.Prev = Prev;
|
||||
|
||||
// Decrement the ref count. In the common case, there's no in-flight invocation for
|
||||
// this handler. However, in the uncommon case that there is, we need to wait for
|
||||
|
|
|
|||
|
|
@ -11,15 +11,12 @@ namespace ModelContextProtocol.Protocol;
|
|||
[JsonConverter(typeof(Converter))]
|
||||
public readonly struct ProgressToken : IEquatable<ProgressToken>
|
||||
{
|
||||
/// <summary>The token, either a string or a boxed long or null.</summary>
|
||||
private readonly object? _token;
|
||||
|
||||
/// <summary>Initializes a new instance of the <see cref="ProgressToken"/> with a specified value.</summary>
|
||||
/// <param name="value">The required ID value.</param>
|
||||
public ProgressToken(string value)
|
||||
{
|
||||
Throw.IfNull(value);
|
||||
_token = value;
|
||||
Token = value;
|
||||
}
|
||||
|
||||
/// <summary>Initializes a new instance of the <see cref="ProgressToken"/> with a specified value.</summary>
|
||||
|
|
@ -27,27 +24,27 @@ public readonly struct ProgressToken : IEquatable<ProgressToken>
|
|||
public ProgressToken(long value)
|
||||
{
|
||||
// Box the long. Progress tokens are almost always strings in practice, so this should be rare.
|
||||
_token = value;
|
||||
Token = value;
|
||||
}
|
||||
|
||||
/// <summary>Gets the underlying object for this token.</summary>
|
||||
/// <remarks>This will either be a <see cref="string"/>, a boxed <see cref="long"/>, or <see langword="null"/>.</remarks>
|
||||
public object? Token => _token;
|
||||
public object? Token { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string? ToString() =>
|
||||
_token is string stringValue ? stringValue :
|
||||
_token is long longValue ? longValue.ToString(CultureInfo.InvariantCulture) :
|
||||
Token is string stringValue ? stringValue :
|
||||
Token is long longValue ? longValue.ToString(CultureInfo.InvariantCulture) :
|
||||
null;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool Equals(ProgressToken other) => Equals(_token, other._token);
|
||||
public bool Equals(ProgressToken other) => Equals(Token, other.Token);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool Equals(object? obj) => obj is ProgressToken other && Equals(other);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override int GetHashCode() => _token?.GetHashCode() ?? 0;
|
||||
public override int GetHashCode() => Token?.GetHashCode() ?? 0;
|
||||
|
||||
/// <inheritdoc />
|
||||
public static bool operator ==(ProgressToken left, ProgressToken right) => left.Equals(right);
|
||||
|
|
@ -77,7 +74,7 @@ public readonly struct ProgressToken : IEquatable<ProgressToken>
|
|||
{
|
||||
Throw.IfNull(writer);
|
||||
|
||||
switch (value._token)
|
||||
switch (value.Token)
|
||||
{
|
||||
case string str:
|
||||
writer.WriteStringValue(str);
|
||||
|
|
|
|||
|
|
@ -11,15 +11,12 @@ namespace ModelContextProtocol.Protocol;
|
|||
[JsonConverter(typeof(Converter))]
|
||||
public readonly struct RequestId : IEquatable<RequestId>
|
||||
{
|
||||
/// <summary>The id, either a string or a boxed long or null.</summary>
|
||||
private readonly object? _id;
|
||||
|
||||
/// <summary>Initializes a new instance of the <see cref="RequestId"/> with a specified value.</summary>
|
||||
/// <param name="value">The required ID value.</param>
|
||||
public RequestId(string value)
|
||||
{
|
||||
Throw.IfNull(value);
|
||||
_id = value;
|
||||
Id = value;
|
||||
}
|
||||
|
||||
/// <summary>Initializes a new instance of the <see cref="RequestId"/> with a specified value.</summary>
|
||||
|
|
@ -27,27 +24,27 @@ public readonly struct RequestId : IEquatable<RequestId>
|
|||
public RequestId(long value)
|
||||
{
|
||||
// Box the long. Request IDs are almost always strings in practice, so this should be rare.
|
||||
_id = value;
|
||||
Id = value;
|
||||
}
|
||||
|
||||
/// <summary>Gets the underlying object for this id.</summary>
|
||||
/// <remarks>This will either be a <see cref="string"/>, a boxed <see cref="long"/>, or <see langword="null"/>.</remarks>
|
||||
public object? Id => _id;
|
||||
public object? Id { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString() =>
|
||||
_id is string stringValue ? stringValue :
|
||||
_id is long longValue ? longValue.ToString(CultureInfo.InvariantCulture) :
|
||||
Id is string stringValue ? stringValue :
|
||||
Id is long longValue ? longValue.ToString(CultureInfo.InvariantCulture) :
|
||||
string.Empty;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool Equals(RequestId other) => Equals(_id, other._id);
|
||||
public bool Equals(RequestId other) => Equals(Id, other.Id);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool Equals(object? obj) => obj is RequestId other && Equals(other);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override int GetHashCode() => _id?.GetHashCode() ?? 0;
|
||||
public override int GetHashCode() => Id?.GetHashCode() ?? 0;
|
||||
|
||||
/// <inheritdoc />
|
||||
public static bool operator ==(RequestId left, RequestId right) => left.Equals(right);
|
||||
|
|
@ -77,7 +74,7 @@ public readonly struct RequestId : IEquatable<RequestId>
|
|||
{
|
||||
Throw.IfNull(writer);
|
||||
|
||||
switch (value._id)
|
||||
switch (value.Id)
|
||||
{
|
||||
case string str:
|
||||
writer.WriteStringValue(str);
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ internal sealed class AIFunctionMcpServerPrompt : McpServerPrompt
|
|||
return Create(
|
||||
AIFunctionFactory.Create(method, args =>
|
||||
{
|
||||
Debug.Assert(args.Services is RequestServiceProvider<GetPromptRequestParams>, $"The service provider should be a {nameof(RequestServiceProvider<GetPromptRequestParams>)} for this method to work correctly.");
|
||||
Debug.Assert(args.Services is RequestServiceProvider<GetPromptRequestParams>, $"The service provider should be a {nameof(RequestServiceProvider<>)} for this method to work correctly.");
|
||||
return createTargetFunc(((RequestServiceProvider<GetPromptRequestParams>)args.Services!).Request);
|
||||
}, CreateAIFunctionFactoryOptions(method, options)),
|
||||
options);
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@ internal sealed class AIFunctionMcpServerResource : McpServerResource
|
|||
return Create(
|
||||
AIFunctionFactory.Create(method, args =>
|
||||
{
|
||||
Debug.Assert(args.Services is RequestServiceProvider<ReadResourceRequestParams>, $"The service provider should be a {nameof(RequestServiceProvider<ReadResourceRequestParams>)} for this method to work correctly.");
|
||||
Debug.Assert(args.Services is RequestServiceProvider<ReadResourceRequestParams>, $"The service provider should be a {nameof(RequestServiceProvider<>)} for this method to work correctly.");
|
||||
return createTargetFunc(((RequestServiceProvider<ReadResourceRequestParams>)args.Services!).Request);
|
||||
}, CreateAIFunctionFactoryOptions(method, options)),
|
||||
options);
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ internal sealed partial class AIFunctionMcpServerTool : McpServerTool
|
|||
return Create(
|
||||
AIFunctionFactory.Create(method, args =>
|
||||
{
|
||||
Debug.Assert(args.Services is RequestServiceProvider<CallToolRequestParams>, $"The service provider should be a {nameof(RequestServiceProvider<CallToolRequestParams>)} for this method to work correctly.");
|
||||
Debug.Assert(args.Services is RequestServiceProvider<CallToolRequestParams>, $"The service provider should be a {nameof(RequestServiceProvider<>)} for this method to work correctly.");
|
||||
return createTargetFunc(((RequestServiceProvider<CallToolRequestParams>)args.Services!).Request);
|
||||
}, CreateAIFunctionFactoryOptions(method, options)),
|
||||
options);
|
||||
|
|
|
|||
|
|
@ -27,8 +27,11 @@ internal sealed class DestinationBoundMcpServer(McpServerImpl server, ITransport
|
|||
throw new ArgumentException("Only transports can provide a JsonRpcMessageContext.");
|
||||
}
|
||||
|
||||
message.Context = new JsonRpcMessageContext();
|
||||
message.Context.RelatedTransport = transport;
|
||||
message.Context = new()
|
||||
{
|
||||
RelatedTransport = transport
|
||||
};
|
||||
|
||||
return server.SendMessageAsync(message, cancellationToken);
|
||||
}
|
||||
|
||||
|
|
@ -39,8 +42,11 @@ internal sealed class DestinationBoundMcpServer(McpServerImpl server, ITransport
|
|||
throw new ArgumentException("Only transports can provide a JsonRpcMessageContext.");
|
||||
}
|
||||
|
||||
request.Context = new JsonRpcMessageContext();
|
||||
request.Context.RelatedTransport = transport;
|
||||
request.Context = new()
|
||||
{
|
||||
RelatedTransport = transport
|
||||
};
|
||||
|
||||
return server.SendRequestAsync(request, cancellationToken);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -341,13 +341,9 @@ public abstract partial class McpServer : McpSession, IMcpServer
|
|||
throw new McpProtocolException(error);
|
||||
}
|
||||
|
||||
var primitiveSchemaDefinition =
|
||||
jsonElement.Deserialize(McpJsonUtilities.JsonContext.Default.PrimitiveSchemaDefinition);
|
||||
|
||||
if (primitiveSchemaDefinition is null)
|
||||
return
|
||||
jsonElement.Deserialize(McpJsonUtilities.JsonContext.Default.PrimitiveSchemaDefinition) ??
|
||||
throw new McpProtocolException($"Type '{type.FullName}' is not a supported property type for elicitation requests.");
|
||||
|
||||
return primitiveSchemaDefinition;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -452,11 +448,9 @@ public abstract partial class McpServer : McpSession, IMcpServer
|
|||
}
|
||||
|
||||
/// <summary>Provides an <see cref="IChatClient"/> implementation that's implemented via client sampling.</summary>
|
||||
private sealed class SamplingChatClient : IChatClient
|
||||
private sealed class SamplingChatClient(McpServer server) : IChatClient
|
||||
{
|
||||
private readonly McpServer _server;
|
||||
|
||||
public SamplingChatClient(McpServer server) => _server = server;
|
||||
private readonly McpServer _server = server;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Task<ChatResponse> GetResponseAsync(IEnumerable<ChatMessage> messages, ChatOptions? options = null, CancellationToken cancellationToken = default) =>
|
||||
|
|
@ -493,11 +487,9 @@ public abstract partial class McpServer : McpSession, IMcpServer
|
|||
/// Provides an <see cref="ILoggerProvider"/> implementation for creating loggers
|
||||
/// that send logging message notifications to the client for logged messages.
|
||||
/// </summary>
|
||||
private sealed class ClientLoggerProvider : ILoggerProvider
|
||||
private sealed class ClientLoggerProvider(McpServer server) : ILoggerProvider
|
||||
{
|
||||
private readonly McpServer _server;
|
||||
|
||||
public ClientLoggerProvider(McpServer server) => _server = server;
|
||||
private readonly McpServer _server = server;
|
||||
|
||||
/// <inheritdoc />
|
||||
public ILogger CreateLogger(string categoryName)
|
||||
|
|
@ -510,16 +502,10 @@ public abstract partial class McpServer : McpSession, IMcpServer
|
|||
/// <inheritdoc />
|
||||
void IDisposable.Dispose() { }
|
||||
|
||||
private sealed class ClientLogger : ILogger
|
||||
private sealed class ClientLogger(McpServer server, string categoryName) : ILogger
|
||||
{
|
||||
private readonly McpServer _server;
|
||||
private readonly string _categoryName;
|
||||
|
||||
public ClientLogger(McpServer server, string categoryName)
|
||||
{
|
||||
_server = server;
|
||||
_categoryName = categoryName;
|
||||
}
|
||||
private readonly McpServer _server = server;
|
||||
private readonly string _categoryName = categoryName;
|
||||
|
||||
/// <inheritdoc />
|
||||
public IDisposable? BeginScope<TState>(TState state) where TState : notnull =>
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ public sealed class McpServerFilters
|
|||
/// Tools from both sources will be combined when returning results to clients.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public List<McpRequestFilter<ListToolsRequestParams, ListToolsResult>> ListToolsFilters { get; } = new();
|
||||
public List<McpRequestFilter<ListToolsRequestParams, ListToolsResult>> ListToolsFilters { get; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Gets the filters for the call tool handler pipeline.
|
||||
|
|
@ -36,7 +36,7 @@ public sealed class McpServerFilters
|
|||
/// The filters can modify, log, or perform additional operations on requests and responses for
|
||||
/// <see cref="RequestMethods.ToolsCall"/> requests. The handler should implement logic to execute the requested tool and return appropriate results.
|
||||
/// </remarks>
|
||||
public List<McpRequestFilter<CallToolRequestParams, CallToolResult>> CallToolFilters { get; } = new();
|
||||
public List<McpRequestFilter<CallToolRequestParams, CallToolResult>> CallToolFilters { get; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Gets the filters for the list prompts handler pipeline.
|
||||
|
|
@ -53,7 +53,7 @@ public sealed class McpServerFilters
|
|||
/// Prompts from both sources will be combined when returning results to clients.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public List<McpRequestFilter<ListPromptsRequestParams, ListPromptsResult>> ListPromptsFilters { get; } = new();
|
||||
public List<McpRequestFilter<ListPromptsRequestParams, ListPromptsResult>> ListPromptsFilters { get; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Gets the filters for the get prompt handler pipeline.
|
||||
|
|
@ -63,7 +63,7 @@ public sealed class McpServerFilters
|
|||
/// The filters can modify, log, or perform additional operations on requests and responses for
|
||||
/// <see cref="RequestMethods.PromptsGet"/> requests. The handler should implement logic to fetch or generate the requested prompt and return appropriate results.
|
||||
/// </remarks>
|
||||
public List<McpRequestFilter<GetPromptRequestParams, GetPromptResult>> GetPromptFilters { get; } = new();
|
||||
public List<McpRequestFilter<GetPromptRequestParams, GetPromptResult>> GetPromptFilters { get; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Gets the filters for the list resource templates handler pipeline.
|
||||
|
|
@ -74,7 +74,7 @@ public sealed class McpServerFilters
|
|||
/// <see cref="RequestMethods.ResourcesTemplatesList"/> requests. It supports pagination through the cursor mechanism,
|
||||
/// where the client can make repeated calls with the cursor returned by the previous call to retrieve more resource templates.
|
||||
/// </remarks>
|
||||
public List<McpRequestFilter<ListResourceTemplatesRequestParams, ListResourceTemplatesResult>> ListResourceTemplatesFilters { get; } = new();
|
||||
public List<McpRequestFilter<ListResourceTemplatesRequestParams, ListResourceTemplatesResult>> ListResourceTemplatesFilters { get; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Gets the filters for the list resources handler pipeline.
|
||||
|
|
@ -85,7 +85,7 @@ public sealed class McpServerFilters
|
|||
/// <see cref="RequestMethods.ResourcesList"/> requests. It supports pagination through the cursor mechanism,
|
||||
/// where the client can make repeated calls with the cursor returned by the previous call to retrieve more resources.
|
||||
/// </remarks>
|
||||
public List<McpRequestFilter<ListResourcesRequestParams, ListResourcesResult>> ListResourcesFilters { get; } = new();
|
||||
public List<McpRequestFilter<ListResourcesRequestParams, ListResourcesResult>> ListResourcesFilters { get; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Gets the filters for the read resource handler pipeline.
|
||||
|
|
@ -95,7 +95,7 @@ public sealed class McpServerFilters
|
|||
/// The filters can modify, log, or perform additional operations on requests and responses for
|
||||
/// <see cref="RequestMethods.ResourcesRead"/> requests. The handler should implement logic to locate and retrieve the requested resource.
|
||||
/// </remarks>
|
||||
public List<McpRequestFilter<ReadResourceRequestParams, ReadResourceResult>> ReadResourceFilters { get; } = new();
|
||||
public List<McpRequestFilter<ReadResourceRequestParams, ReadResourceResult>> ReadResourceFilters { get; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Gets the filters for the complete handler pipeline.
|
||||
|
|
@ -106,7 +106,7 @@ public sealed class McpServerFilters
|
|||
/// <see cref="RequestMethods.CompletionComplete"/> requests. The handler processes auto-completion requests, returning a list of suggestions based on the
|
||||
/// reference type and current argument value.
|
||||
/// </remarks>
|
||||
public List<McpRequestFilter<CompleteRequestParams, CompleteResult>> CompleteFilters { get; } = new();
|
||||
public List<McpRequestFilter<CompleteRequestParams, CompleteResult>> CompleteFilters { get; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Gets the filters for the subscribe to resources handler pipeline.
|
||||
|
|
@ -123,7 +123,7 @@ public sealed class McpServerFilters
|
|||
/// whenever a relevant resource is created, updated, or deleted.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public List<McpRequestFilter<SubscribeRequestParams, EmptyResult>> SubscribeToResourcesFilters { get; } = new();
|
||||
public List<McpRequestFilter<SubscribeRequestParams, EmptyResult>> SubscribeToResourcesFilters { get; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Gets the filters for the unsubscribe from resources handler pipeline.
|
||||
|
|
@ -140,7 +140,7 @@ public sealed class McpServerFilters
|
|||
/// to the client for the specified resources.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public List<McpRequestFilter<UnsubscribeRequestParams, EmptyResult>> UnsubscribeFromResourcesFilters { get; } = new();
|
||||
public List<McpRequestFilter<UnsubscribeRequestParams, EmptyResult>> UnsubscribeFromResourcesFilters { get; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Gets the filters for the set logging level handler pipeline.
|
||||
|
|
@ -157,5 +157,5 @@ public sealed class McpServerFilters
|
|||
/// at or above the specified level to the client as notifications/message notifications.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public List<McpRequestFilter<SetLevelRequestParams, EmptyResult>> SetLoggingLevelFilters { get; } = new();
|
||||
public List<McpRequestFilter<SetLevelRequestParams, EmptyResult>> SetLoggingLevelFilters { get; } = [];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -207,12 +207,9 @@ internal sealed partial class McpServerImpl : McpServer
|
|||
// Otherwise, try to use whatever the client requested as long as it's supported.
|
||||
// If it's not supported, fall back to the latest supported version.
|
||||
string? protocolVersion = options.ProtocolVersion;
|
||||
if (protocolVersion is null)
|
||||
{
|
||||
protocolVersion = request?.ProtocolVersion is string clientProtocolVersion && McpSessionHandler.SupportedProtocolVersions.Contains(clientProtocolVersion) ?
|
||||
clientProtocolVersion :
|
||||
McpSessionHandler.LatestProtocolVersion;
|
||||
}
|
||||
protocolVersion ??= request?.ProtocolVersion is string clientProtocolVersion && McpSessionHandler.SupportedProtocolVersions.Contains(clientProtocolVersion) ?
|
||||
clientProtocolVersion :
|
||||
McpSessionHandler.LatestProtocolVersion;
|
||||
|
||||
_negotiatedProtocolVersion = protocolVersion;
|
||||
|
||||
|
|
|
|||
|
|
@ -7,8 +7,6 @@ namespace ModelContextProtocol.Server;
|
|||
/// </summary>
|
||||
public sealed class McpServerOptions
|
||||
{
|
||||
private McpServerHandlers? _handlers;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets information about this server implementation, including its name and version.
|
||||
/// </summary>
|
||||
|
|
@ -97,11 +95,11 @@ public sealed class McpServerOptions
|
|||
/// </summary>
|
||||
public McpServerHandlers Handlers
|
||||
{
|
||||
get => _handlers ??= new();
|
||||
get => field ??= new();
|
||||
set
|
||||
{
|
||||
Throw.IfNull(value);
|
||||
_handlers = value;
|
||||
field = value;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ public sealed class McpServerPromptCreateOptions
|
|||
/// Creates a shallow clone of the current <see cref="McpServerPromptCreateOptions"/> instance.
|
||||
/// </summary>
|
||||
internal McpServerPromptCreateOptions Clone() =>
|
||||
new McpServerPromptCreateOptions
|
||||
new()
|
||||
{
|
||||
Services = Services,
|
||||
Name = Name,
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue