Add net10.0 assets and run some fixers (#875)
This commit is contained in:
parent
e7b403b820
commit
bfc0980ef5
46 changed files with 168 additions and 194 deletions
|
|
@ -1,9 +1,9 @@
|
||||||
<Project>
|
<Project>
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
||||||
<System9Version>9.0.5</System9Version>
|
<System9Version>9.0.10</System9Version>
|
||||||
<System10Version>10.0.0-preview.4.25258.110</System10Version>
|
<System10Version>10.0.0-rc.2.25502.107</System10Version>
|
||||||
<MicrosoftExtensionsAIVersion>9.9.1</MicrosoftExtensionsAIVersion>
|
<MicrosoftExtensionsAIVersion>9.10.0</MicrosoftExtensionsAIVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<!-- Product dependencies netstandard -->
|
<!-- Product dependencies netstandard -->
|
||||||
|
|
@ -28,19 +28,17 @@
|
||||||
<!-- Product dependencies .NET 9 -->
|
<!-- Product dependencies .NET 9 -->
|
||||||
<ItemGroup Condition="'$(TargetFramework)' == 'net9.0'">
|
<ItemGroup Condition="'$(TargetFramework)' == 'net9.0'">
|
||||||
<PackageVersion Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="$(System9Version)" />
|
<PackageVersion Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="$(System9Version)" />
|
||||||
<PackageVersion Include="Microsoft.IdentityModel.Tokens" Version="8.9.0" />
|
<PackageVersion Include="Microsoft.IdentityModel.Tokens" Version="8.14.0" />
|
||||||
<PackageVersion Include="Microsoft.Extensions.Hosting.Abstractions" Version="$(System9Version)" />
|
<PackageVersion Include="Microsoft.Extensions.Hosting.Abstractions" Version="$(System9Version)" />
|
||||||
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="$(System9Version)" />
|
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="$(System9Version)" />
|
||||||
<PackageVersion Include="System.IO.Pipelines" Version="$(System9Version)" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<!-- Product dependencies .NET 10 -->
|
<!-- Product dependencies .NET 10 -->
|
||||||
<ItemGroup Condition="'$(TargetFramework)' == 'net10.0'">
|
<ItemGroup Condition="'$(TargetFramework)' == 'net10.0'">
|
||||||
<PackageVersion Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="$(System10Version)" />
|
<PackageVersion Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="$(System10Version)" />
|
||||||
<PackageVersion Include="Microsoft.IdentityModel.Tokens" Version="8.9.0" />
|
<PackageVersion Include="Microsoft.IdentityModel.Tokens" Version="8.14.0" />
|
||||||
<PackageVersion Include="Microsoft.Extensions.Hosting.Abstractions" Version="$(System10Version)" />
|
<PackageVersion Include="Microsoft.Extensions.Hosting.Abstractions" Version="$(System10Version)" />
|
||||||
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="$(System10Version)" />
|
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="$(System10Version)" />
|
||||||
<PackageVersion Include="System.IO.Pipelines" Version="$(System10Version)" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<!-- Product dependencies shared -->
|
<!-- Product dependencies shared -->
|
||||||
|
|
@ -56,19 +54,19 @@
|
||||||
<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="8.0.0" />
|
<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="8.0.0" />
|
||||||
|
|
||||||
<!-- Testing dependencies -->
|
<!-- Testing dependencies -->
|
||||||
<PackageVersion Include="Anthropic.SDK" Version="5.5.1" />
|
<PackageVersion Include="Anthropic.SDK" Version="5.6.0" />
|
||||||
<PackageVersion Include="coverlet.collector" Version="6.0.4">
|
<PackageVersion Include="coverlet.collector" Version="6.0.4">
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
</PackageVersion>
|
</PackageVersion>
|
||||||
<PackageVersion Include="GitHubActionsTestLogger" Version="2.4.1" />
|
<PackageVersion Include="GitHubActionsTestLogger" Version="2.4.1" />
|
||||||
<PackageVersion Include="Microsoft.Extensions.AI.OpenAI" Version="9.9.1-preview.1.25474.6" />
|
<PackageVersion Include="Microsoft.Extensions.AI.OpenAI" Version="9.10.0-preview.1.25513.3" />
|
||||||
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="$(System9Version)" />
|
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="$(System9Version)" />
|
||||||
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="$(System9Version)" />
|
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="$(System9Version)" />
|
||||||
<PackageVersion Include="Microsoft.Extensions.Logging" Version="$(System9Version)" />
|
<PackageVersion Include="Microsoft.Extensions.Logging" Version="$(System9Version)" />
|
||||||
<PackageVersion Include="Microsoft.Extensions.Logging.Console" Version="$(System9Version)" />
|
<PackageVersion Include="Microsoft.Extensions.Logging.Console" Version="$(System9Version)" />
|
||||||
<PackageVersion Include="Microsoft.Extensions.Options" Version="$(System9Version)" />
|
<PackageVersion Include="Microsoft.Extensions.Options" Version="$(System9Version)" />
|
||||||
<PackageVersion Include="Microsoft.Extensions.TimeProvider.Testing" Version="9.5.0" />
|
<PackageVersion Include="Microsoft.Extensions.TimeProvider.Testing" Version="9.10.0" />
|
||||||
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
|
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
|
||||||
<PackageVersion Include="Moq" Version="4.20.72" />
|
<PackageVersion Include="Moq" Version="4.20.72" />
|
||||||
<PackageVersion Include="OpenTelemetry" Version="1.12.0" />
|
<PackageVersion Include="OpenTelemetry" Version="1.12.0" />
|
||||||
|
|
|
||||||
|
|
@ -73,7 +73,7 @@ public sealed class InteractiveTools
|
||||||
string? playerName = nameResponse.Content?["Name"].GetString();
|
string? playerName = nameResponse.Content?["Name"].GetString();
|
||||||
|
|
||||||
// Generate a random number between 1 and 10
|
// Generate a random number between 1 and 10
|
||||||
Random random = new Random();
|
Random random = new();
|
||||||
int targetNumber = random.Next(1, 11); // 1 to 10 inclusive
|
int targetNumber = random.Next(1, 11); // 1 to 10 inclusive
|
||||||
int attempts = 0;
|
int attempts = 0;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ builder.Services.AddMcpServer()
|
||||||
{
|
{
|
||||||
mcpOptions.Capabilities = new();
|
mcpOptions.Capabilities = new();
|
||||||
mcpOptions.Capabilities.Tools = new();
|
mcpOptions.Capabilities.Tools = new();
|
||||||
var toolCollection = mcpOptions.ToolCollection = new();
|
var toolCollection = mcpOptions.ToolCollection = [];
|
||||||
|
|
||||||
foreach (var tool in tools)
|
foreach (var tool in tools)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -350,7 +350,7 @@ internal class CancellableStreamReader : TextReader
|
||||||
CheckAsyncTaskInProgress();
|
CheckAsyncTaskInProgress();
|
||||||
|
|
||||||
// Call ReadBuffer, then pull data out of charBuffer.
|
// Call ReadBuffer, then pull data out of charBuffer.
|
||||||
StringBuilder sb = new StringBuilder(_charLen - _charPos);
|
StringBuilder sb = new(_charLen - _charPos);
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
sb.Append(_charBuffer, _charPos, _charLen - _charPos);
|
sb.Append(_charBuffer, _charPos, _charLen - _charPos);
|
||||||
|
|
@ -953,7 +953,7 @@ internal class CancellableStreamReader : TextReader
|
||||||
private async Task<string> ReadToEndAsyncInternal(CancellationToken cancellationToken)
|
private async Task<string> ReadToEndAsyncInternal(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
// Call ReadBuffer, then pull data out of charBuffer.
|
// Call ReadBuffer, then pull data out of charBuffer.
|
||||||
StringBuilder sb = new StringBuilder(_charLen - _charPos);
|
StringBuilder sb = new(_charLen - _charPos);
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
int tmpCharPos = _charPos;
|
int tmpCharPos = _charPos;
|
||||||
|
|
|
||||||
|
|
@ -26,8 +26,8 @@ public class McpAuthenticationOptions : AuthenticationSchemeOptions
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public new McpAuthenticationEvents Events
|
public new McpAuthenticationEvents Events
|
||||||
{
|
{
|
||||||
get { return (McpAuthenticationEvents)base.Events!; }
|
get => (McpAuthenticationEvents)base.Events!;
|
||||||
set { base.Events = value; }
|
set => base.Events = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,7 @@ internal sealed class AuthorizationFilterSetup(IAuthorizationPolicyProvider? pol
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CheckListToolsFilter(McpServerOptions options)
|
private static void CheckListToolsFilter(McpServerOptions options)
|
||||||
{
|
{
|
||||||
options.Filters.ListToolsFilters.Add(next => async (context, cancellationToken) =>
|
options.Filters.ListToolsFilters.Add(next => async (context, cancellationToken) =>
|
||||||
{
|
{
|
||||||
|
|
@ -87,7 +87,7 @@ internal sealed class AuthorizationFilterSetup(IAuthorizationPolicyProvider? pol
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CheckCallToolFilter(McpServerOptions options)
|
private static void CheckCallToolFilter(McpServerOptions options)
|
||||||
{
|
{
|
||||||
options.Filters.CallToolFilters.Add(next => async (context, cancellationToken) =>
|
options.Filters.CallToolFilters.Add(next => async (context, cancellationToken) =>
|
||||||
{
|
{
|
||||||
|
|
@ -115,7 +115,7 @@ internal sealed class AuthorizationFilterSetup(IAuthorizationPolicyProvider? pol
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CheckListResourcesFilter(McpServerOptions options)
|
private static void CheckListResourcesFilter(McpServerOptions options)
|
||||||
{
|
{
|
||||||
options.Filters.ListResourcesFilters.Add(next => async (context, cancellationToken) =>
|
options.Filters.ListResourcesFilters.Add(next => async (context, cancellationToken) =>
|
||||||
{
|
{
|
||||||
|
|
@ -145,7 +145,7 @@ internal sealed class AuthorizationFilterSetup(IAuthorizationPolicyProvider? pol
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CheckListResourceTemplatesFilter(McpServerOptions options)
|
private static void CheckListResourceTemplatesFilter(McpServerOptions options)
|
||||||
{
|
{
|
||||||
options.Filters.ListResourceTemplatesFilters.Add(next => async (context, cancellationToken) =>
|
options.Filters.ListResourceTemplatesFilters.Add(next => async (context, cancellationToken) =>
|
||||||
{
|
{
|
||||||
|
|
@ -177,7 +177,7 @@ internal sealed class AuthorizationFilterSetup(IAuthorizationPolicyProvider? pol
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CheckReadResourceFilter(McpServerOptions options)
|
private static void CheckReadResourceFilter(McpServerOptions options)
|
||||||
{
|
{
|
||||||
options.Filters.ReadResourceFilters.Add(next => async (context, cancellationToken) =>
|
options.Filters.ReadResourceFilters.Add(next => async (context, cancellationToken) =>
|
||||||
{
|
{
|
||||||
|
|
@ -205,7 +205,7 @@ internal sealed class AuthorizationFilterSetup(IAuthorizationPolicyProvider? pol
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CheckListPromptsFilter(McpServerOptions options)
|
private static void CheckListPromptsFilter(McpServerOptions options)
|
||||||
{
|
{
|
||||||
options.Filters.ListPromptsFilters.Add(next => async (context, cancellationToken) =>
|
options.Filters.ListPromptsFilters.Add(next => async (context, cancellationToken) =>
|
||||||
{
|
{
|
||||||
|
|
@ -237,7 +237,7 @@ internal sealed class AuthorizationFilterSetup(IAuthorizationPolicyProvider? pol
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CheckGetPromptFilter(McpServerOptions options)
|
private static void CheckGetPromptFilter(McpServerOptions options)
|
||||||
{
|
{
|
||||||
options.Filters.GetPromptFilters.Add(next => async (context, cancellationToken) =>
|
options.Filters.GetPromptFilters.Add(next => async (context, cancellationToken) =>
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFrameworks>net9.0;net8.0</TargetFrameworks>
|
<TargetFrameworks>net10.0;net9.0;net8.0</TargetFrameworks>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||||
|
|
|
||||||
|
|
@ -128,10 +128,7 @@ public static class AIContentExtensions
|
||||||
_ => null,
|
_ => null,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (ac is not null)
|
ac?.RawRepresentation = content;
|
||||||
{
|
|
||||||
ac.RawRepresentation = content;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ac;
|
return ac;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,8 @@
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Microsoft.Extensions.Logging.Abstractions;
|
using Microsoft.Extensions.Logging.Abstractions;
|
||||||
|
#if NET9_0_OR_GREATER
|
||||||
|
using System.Buffers.Text;
|
||||||
|
#endif
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Net.Http.Headers;
|
using System.Net.Http.Headers;
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
|
|
@ -238,14 +241,17 @@ internal sealed partial class ClientOAuthProvider
|
||||||
LogOAuthAuthorizationCompleted();
|
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)
|
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
|
try
|
||||||
{
|
{
|
||||||
|
|
@ -540,11 +546,7 @@ internal sealed partial class ClientOAuthProvider
|
||||||
Port = -1 // Always remove port
|
Port = -1 // Always remove port
|
||||||
};
|
};
|
||||||
|
|
||||||
if (builder.Path == "/")
|
if (builder.Path.Length > 0 && builder.Path[builder.Path.Length - 1] == '/')
|
||||||
{
|
|
||||||
builder.Path = string.Empty;
|
|
||||||
}
|
|
||||||
else if (builder.Path.Length > 1 && builder.Path.EndsWith("/"))
|
|
||||||
{
|
{
|
||||||
builder.Path = builder.Path.TrimEnd('/');
|
builder.Path = builder.Path.TrimEnd('/');
|
||||||
}
|
}
|
||||||
|
|
@ -633,18 +635,18 @@ internal sealed partial class ClientOAuthProvider
|
||||||
continue;
|
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)
|
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();
|
using var sha256 = SHA256.Create();
|
||||||
var challengeBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(codeVerifier));
|
var challengeBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(codeVerifier));
|
||||||
return Convert.ToBase64String(challengeBytes)
|
return Convert.ToBase64String(challengeBytes)
|
||||||
.TrimEnd('=')
|
.TrimEnd('=')
|
||||||
.Replace('+', '-')
|
.Replace('+', '-')
|
||||||
.Replace('/', '_');
|
.Replace('/', '_');
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
private string GetClientIdOrThrow() => _clientId ?? throw new InvalidOperationException("Client ID is not available. This may indicate an issue with dynamic client registration.");
|
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>
|
/// </remarks>
|
||||||
public sealed class McpClientOptions
|
public sealed class McpClientOptions
|
||||||
{
|
{
|
||||||
private McpClientHandlers? _handlers;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets information about this client implementation, including its name and version.
|
/// Gets or sets information about this client implementation, including its name and version.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -71,11 +69,11 @@ public sealed class McpClientOptions
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public McpClientHandlers Handlers
|
public McpClientHandlers Handlers
|
||||||
{
|
{
|
||||||
get => _handlers ??= new();
|
get => field ??= new();
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
Throw.IfNull(value);
|
Throw.IfNull(value);
|
||||||
_handlers = value;
|
field = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,21 +5,15 @@ using System.Diagnostics;
|
||||||
namespace ModelContextProtocol.Client;
|
namespace ModelContextProtocol.Client;
|
||||||
|
|
||||||
/// <summary>Provides the client side of a stdio-based session transport.</summary>
|
/// <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 StdioClientTransportOptions _options = options;
|
||||||
private readonly Process _process;
|
private readonly Process _process = process;
|
||||||
private readonly Queue<string> _stderrRollingLog;
|
private readonly Queue<string> _stderrRollingLog = stderrRollingLog;
|
||||||
private int _cleanedUp = 0;
|
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/>
|
/// <inheritdoc/>
|
||||||
public override async Task SendMessageAsync(JsonRpcMessage message, CancellationToken cancellationToken = default)
|
public override async Task SendMessageAsync(JsonRpcMessage message, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
|
|
@ -56,7 +50,7 @@ internal sealed class StdioClientSessionTransport : StreamClientSessionTransport
|
||||||
// Now terminate the server process.
|
// Now terminate the server process.
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
StdioClientTransport.DisposeProcess(_process, processRunning: true, _options.ShutdownTimeout, Name);
|
StdioClientTransport.DisposeProcess(_process, processRunning: true, shutdownTimeout: _options.ShutdownTimeout);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,7 @@ public sealed partial class StdioClientTransport : IClientTransport
|
||||||
|
|
||||||
_options = options;
|
_options = options;
|
||||||
_loggerFactory = loggerFactory;
|
_loggerFactory = loggerFactory;
|
||||||
Name = options.Name ?? $"stdio-{Regex.Replace(Path.GetFileName(options.Command), @"[\s\.]+", "-")}";
|
Name = options.Name ?? $"stdio-{WhitespaceAndPeriods().Replace(Path.GetFileName(options.Command), "-")}";
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
|
@ -189,7 +189,7 @@ public sealed partial class StdioClientTransport : IClientTransport
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
DisposeProcess(process, processStarted, _options.ShutdownTimeout, endpointName);
|
DisposeProcess(process, processStarted, _options.ShutdownTimeout);
|
||||||
}
|
}
|
||||||
catch (Exception ex2)
|
catch (Exception ex2)
|
||||||
{
|
{
|
||||||
|
|
@ -201,7 +201,7 @@ public sealed partial class StdioClientTransport : IClientTransport
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static void DisposeProcess(
|
internal static void DisposeProcess(
|
||||||
Process? process, bool processRunning, TimeSpan shutdownTimeout, string endpointName)
|
Process? process, bool processRunning, TimeSpan shutdownTimeout)
|
||||||
{
|
{
|
||||||
if (process is not null)
|
if (process is not null)
|
||||||
{
|
{
|
||||||
|
|
@ -279,4 +279,12 @@ public sealed partial class StdioClientTransport : IClientTransport
|
||||||
|
|
||||||
[LoggerMessage(Level = LogLevel.Warning, Message = "{EndpointName} shutdown failed.")]
|
[LoggerMessage(Level = LogLevel.Warning, Message = "{EndpointName} shutdown failed.")]
|
||||||
private static partial void LogTransportShutdownFailed(ILogger logger, string endpointName, Exception exception);
|
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)
|
if (jsonObject["_meta"] is not JsonObject meta)
|
||||||
{
|
{
|
||||||
meta = new JsonObject();
|
jsonObject["_meta"] = meta = [];
|
||||||
jsonObject["_meta"] = meta;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
meta[key] = value;
|
meta[key] = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -405,7 +405,7 @@ internal sealed partial class McpSessionHandler : IAsyncDisposable
|
||||||
|
|
||||||
long? startingTimestamp = durationMetric.Enabled ? Stopwatch.GetTimestamp() : null;
|
long? startingTimestamp = durationMetric.Enabled ? Stopwatch.GetTimestamp() : null;
|
||||||
using Activity? activity = Diagnostics.ShouldInstrumentMessage(request) ?
|
using Activity? activity = Diagnostics.ShouldInstrumentMessage(request) ?
|
||||||
Diagnostics.ActivitySource.StartActivity(CreateActivityName(method), ActivityKind.Client) :
|
Diagnostics.ActivitySource.StartActivity(McpSessionHandler.CreateActivityName(method), ActivityKind.Client) :
|
||||||
null;
|
null;
|
||||||
|
|
||||||
// Set request ID
|
// Set request ID
|
||||||
|
|
@ -501,7 +501,7 @@ internal sealed partial class McpSessionHandler : IAsyncDisposable
|
||||||
|
|
||||||
long? startingTimestamp = durationMetric.Enabled ? Stopwatch.GetTimestamp() : null;
|
long? startingTimestamp = durationMetric.Enabled ? Stopwatch.GetTimestamp() : null;
|
||||||
using Activity? activity = Diagnostics.ShouldInstrumentMessage(message) ?
|
using Activity? activity = Diagnostics.ShouldInstrumentMessage(message) ?
|
||||||
Diagnostics.ActivitySource.StartActivity(CreateActivityName(method), ActivityKind.Client) :
|
Diagnostics.ActivitySource.StartActivity(McpSessionHandler.CreateActivityName(method), ActivityKind.Client) :
|
||||||
null;
|
null;
|
||||||
|
|
||||||
TagList tags = default;
|
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) =>
|
private static string GetMethodName(JsonRpcMessage message) =>
|
||||||
message switch
|
message switch
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFrameworks>net9.0;net8.0;netstandard2.0</TargetFrameworks>
|
<TargetFrameworks>net10.0;net9.0;net8.0;netstandard2.0</TargetFrameworks>
|
||||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||||
<IsPackable>true</IsPackable>
|
<IsPackable>true</IsPackable>
|
||||||
<PackageId>ModelContextProtocol.Core</PackageId>
|
<PackageId>ModelContextProtocol.Core</PackageId>
|
||||||
|
|
@ -27,16 +27,20 @@
|
||||||
<PackageReference Include="System.Threading.Channels" />
|
<PackageReference Include="System.Threading.Channels" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<!-- Dependencies only needed by netstandard2.0 or net8.0 -->
|
<!-- Dependencies only needed by pre-net9.0 -->
|
||||||
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0' or '$(TargetFramework)' == 'net8.0'">
|
<ItemGroup Condition="!$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', 'net9.0'))">
|
||||||
<PackageReference Include="System.IO.Pipelines" />
|
<PackageReference Include="System.IO.Pipelines" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<!-- Dependencies only needed pre-net10.0 -->
|
||||||
|
<ItemGroup Condition="!$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', 'net10.0'))">
|
||||||
|
<PackageReference Include="System.Net.ServerSentEvents" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
<!-- Dependencies needed by all -->
|
<!-- Dependencies needed by all -->
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.Extensions.AI.Abstractions" />
|
<PackageReference Include="Microsoft.Extensions.AI.Abstractions" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" />
|
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" />
|
||||||
<PackageReference Include="System.Net.ServerSentEvents" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<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.
|
// 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
|
// We want to ensure that an enumeration through all of the registrations can still
|
||||||
// progress through this one.
|
// progress through this one.
|
||||||
if (Prev is not null)
|
Prev?.Next = Next;
|
||||||
{
|
Next?.Prev = Prev;
|
||||||
Prev.Next = Next;
|
|
||||||
}
|
|
||||||
if (Next is not null)
|
|
||||||
{
|
|
||||||
Next.Prev = Prev;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decrement the ref count. In the common case, there's no in-flight invocation for
|
// 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
|
// 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))]
|
[JsonConverter(typeof(Converter))]
|
||||||
public readonly struct ProgressToken : IEquatable<ProgressToken>
|
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>
|
/// <summary>Initializes a new instance of the <see cref="ProgressToken"/> with a specified value.</summary>
|
||||||
/// <param name="value">The required ID value.</param>
|
/// <param name="value">The required ID value.</param>
|
||||||
public ProgressToken(string value)
|
public ProgressToken(string value)
|
||||||
{
|
{
|
||||||
Throw.IfNull(value);
|
Throw.IfNull(value);
|
||||||
_token = value;
|
Token = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Initializes a new instance of the <see cref="ProgressToken"/> with a specified value.</summary>
|
/// <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)
|
public ProgressToken(long value)
|
||||||
{
|
{
|
||||||
// Box the long. Progress tokens are almost always strings in practice, so this should be rare.
|
// 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>
|
/// <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>
|
/// <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 />
|
/// <inheritdoc />
|
||||||
public override string? ToString() =>
|
public override string? ToString() =>
|
||||||
_token is string stringValue ? stringValue :
|
Token is string stringValue ? stringValue :
|
||||||
_token is long longValue ? longValue.ToString(CultureInfo.InvariantCulture) :
|
Token is long longValue ? longValue.ToString(CultureInfo.InvariantCulture) :
|
||||||
null;
|
null;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public bool Equals(ProgressToken other) => Equals(_token, other._token);
|
public bool Equals(ProgressToken other) => Equals(Token, other.Token);
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override bool Equals(object? obj) => obj is ProgressToken other && Equals(other);
|
public override bool Equals(object? obj) => obj is ProgressToken other && Equals(other);
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override int GetHashCode() => _token?.GetHashCode() ?? 0;
|
public override int GetHashCode() => Token?.GetHashCode() ?? 0;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public static bool operator ==(ProgressToken left, ProgressToken right) => left.Equals(right);
|
public static bool operator ==(ProgressToken left, ProgressToken right) => left.Equals(right);
|
||||||
|
|
@ -77,7 +74,7 @@ public readonly struct ProgressToken : IEquatable<ProgressToken>
|
||||||
{
|
{
|
||||||
Throw.IfNull(writer);
|
Throw.IfNull(writer);
|
||||||
|
|
||||||
switch (value._token)
|
switch (value.Token)
|
||||||
{
|
{
|
||||||
case string str:
|
case string str:
|
||||||
writer.WriteStringValue(str);
|
writer.WriteStringValue(str);
|
||||||
|
|
|
||||||
|
|
@ -11,15 +11,12 @@ namespace ModelContextProtocol.Protocol;
|
||||||
[JsonConverter(typeof(Converter))]
|
[JsonConverter(typeof(Converter))]
|
||||||
public readonly struct RequestId : IEquatable<RequestId>
|
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>
|
/// <summary>Initializes a new instance of the <see cref="RequestId"/> with a specified value.</summary>
|
||||||
/// <param name="value">The required ID value.</param>
|
/// <param name="value">The required ID value.</param>
|
||||||
public RequestId(string value)
|
public RequestId(string value)
|
||||||
{
|
{
|
||||||
Throw.IfNull(value);
|
Throw.IfNull(value);
|
||||||
_id = value;
|
Id = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Initializes a new instance of the <see cref="RequestId"/> with a specified value.</summary>
|
/// <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)
|
public RequestId(long value)
|
||||||
{
|
{
|
||||||
// Box the long. Request IDs are almost always strings in practice, so this should be rare.
|
// 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>
|
/// <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>
|
/// <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 />
|
/// <inheritdoc />
|
||||||
public override string ToString() =>
|
public override string ToString() =>
|
||||||
_id is string stringValue ? stringValue :
|
Id is string stringValue ? stringValue :
|
||||||
_id is long longValue ? longValue.ToString(CultureInfo.InvariantCulture) :
|
Id is long longValue ? longValue.ToString(CultureInfo.InvariantCulture) :
|
||||||
string.Empty;
|
string.Empty;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public bool Equals(RequestId other) => Equals(_id, other._id);
|
public bool Equals(RequestId other) => Equals(Id, other.Id);
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override bool Equals(object? obj) => obj is RequestId other && Equals(other);
|
public override bool Equals(object? obj) => obj is RequestId other && Equals(other);
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override int GetHashCode() => _id?.GetHashCode() ?? 0;
|
public override int GetHashCode() => Id?.GetHashCode() ?? 0;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public static bool operator ==(RequestId left, RequestId right) => left.Equals(right);
|
public static bool operator ==(RequestId left, RequestId right) => left.Equals(right);
|
||||||
|
|
@ -77,7 +74,7 @@ public readonly struct RequestId : IEquatable<RequestId>
|
||||||
{
|
{
|
||||||
Throw.IfNull(writer);
|
Throw.IfNull(writer);
|
||||||
|
|
||||||
switch (value._id)
|
switch (value.Id)
|
||||||
{
|
{
|
||||||
case string str:
|
case string str:
|
||||||
writer.WriteStringValue(str);
|
writer.WriteStringValue(str);
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,7 @@ internal sealed class AIFunctionMcpServerPrompt : McpServerPrompt
|
||||||
return Create(
|
return Create(
|
||||||
AIFunctionFactory.Create(method, args =>
|
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);
|
return createTargetFunc(((RequestServiceProvider<GetPromptRequestParams>)args.Services!).Request);
|
||||||
}, CreateAIFunctionFactoryOptions(method, options)),
|
}, CreateAIFunctionFactoryOptions(method, options)),
|
||||||
options);
|
options);
|
||||||
|
|
|
||||||
|
|
@ -67,7 +67,7 @@ internal sealed class AIFunctionMcpServerResource : McpServerResource
|
||||||
return Create(
|
return Create(
|
||||||
AIFunctionFactory.Create(method, args =>
|
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);
|
return createTargetFunc(((RequestServiceProvider<ReadResourceRequestParams>)args.Services!).Request);
|
||||||
}, CreateAIFunctionFactoryOptions(method, options)),
|
}, CreateAIFunctionFactoryOptions(method, options)),
|
||||||
options);
|
options);
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue