Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 39 additions & 11 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@
<System9Version>9.0.11</System9Version>
<System10Version>10.0.3</System10Version>
<MicrosoftExtensionsVersion>10.3.0</MicrosoftExtensionsVersion>
<MicrosoftExtensionsAI9Version>9.10.2</MicrosoftExtensionsAI9Version>
</PropertyGroup>

<!-- Product dependencies shared -->
<ItemGroup>
<!-- Product dependencies shared (non-net8.0) -->
<ItemGroup Condition="'$(TargetFramework)' != 'net8.0'">
<PackageVersion Include="Microsoft.Extensions.AI" Version="$(MicrosoftExtensionsVersion)" />
<PackageVersion Include="Microsoft.Extensions.AI.Abstractions" Version="$(MicrosoftExtensionsVersion)" />
<PackageVersion Include="Microsoft.Extensions.Caching.Abstractions" Version="$(System10Version)" />
Expand All @@ -17,6 +18,16 @@
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="$(System10Version)" />
</ItemGroup>

<!-- Product dependencies shared (net8.0) -->
<ItemGroup Condition="'$(TargetFramework)' == 'net8.0'">
<PackageVersion Include="Microsoft.Extensions.AI" Version="$(MicrosoftExtensionsAI9Version)" />
<PackageVersion Include="Microsoft.Extensions.AI.Abstractions" Version="$(MicrosoftExtensionsAI9Version)" />
<PackageVersion Include="Microsoft.Extensions.Caching.Abstractions" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.Caching.Memory" Version="8.0.1" />
<PackageVersion Include="Microsoft.Extensions.Hosting.Abstractions" Version="8.0.1" />
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.3" />
</ItemGroup>

<!-- Product dependencies < .NET 10 -->
<ItemGroup Condition="!$([MSBuild]::IsTargetFrameworkCompatible($(TargetFramework), 'net10.0'))">
<PackageVersion Include="System.Net.ServerSentEvents" Version="$(System10Version)" />
Expand All @@ -34,6 +45,7 @@
<!-- Product dependencies .NET 8 -->
<ItemGroup Condition="'$(TargetFramework)' == 'net8.0'">
<PackageVersion Include="System.IO.Pipelines" Version="$(System10Version)" />
<PackageVersion Include="System.Text.Json" Version="$(System10Version)" />
<PackageVersion Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="$(System8Version)" />
</ItemGroup>

Expand Down Expand Up @@ -67,13 +79,6 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageVersion>
<PackageVersion Include="Microsoft.Extensions.AI.OpenAI" Version="10.2.0-preview.1.26063.2" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="$(System10Version)" />
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="$(System10Version)" />
<PackageVersion Include="Microsoft.Extensions.Logging" Version="$(System10Version)" />
<PackageVersion Include="Microsoft.Extensions.Logging.Console" Version="$(System10Version)" />
<PackageVersion Include="Microsoft.Extensions.Options" Version="$(System10Version)" />
<PackageVersion Include="Microsoft.Extensions.TimeProvider.Testing" Version="10.1.0" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.0.1" />
<PackageVersion Include="Moq" Version="4.20.72" />
<PackageVersion Include="OpenTelemetry" Version="1.15.0" />
Expand All @@ -82,8 +87,10 @@
<PackageVersion Include="OpenTelemetry.Instrumentation.Http" Version="1.15.0" />
<PackageVersion Include="OpenTelemetry.Extensions.Hosting" Version="1.15.0" />
<PackageVersion Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.15.0" />
<PackageVersion Include="Serilog.Extensions.Hosting" Version="10.0.0" />
<PackageVersion Include="Serilog.Extensions.Logging" Version="10.0.0" />
<PackageVersion Include="Serilog.Extensions.Hosting" Version="10.0.0" Condition="'$(TargetFramework)' != 'net8.0'" />
<PackageVersion Include="Serilog.Extensions.Hosting" Version="8.0.0" Condition="'$(TargetFramework)' == 'net8.0'" />
<PackageVersion Include="Serilog.Extensions.Logging" Version="10.0.0" Condition="'$(TargetFramework)' != 'net8.0'" />
<PackageVersion Include="Serilog.Extensions.Logging" Version="8.0.0" Condition="'$(TargetFramework)' == 'net8.0'" />
<PackageVersion Include="Serilog.Sinks.Console" Version="6.1.1" />
<PackageVersion Include="Serilog.Sinks.Debug" Version="3.0.0" />
<PackageVersion Include="Serilog.Sinks.File" Version="7.0.0" />
Expand All @@ -94,4 +101,25 @@
<PackageVersion Include="System.Net.Http" Version="4.3.4" />
<PackageVersion Include="JsonSchema.Net" Version="9.1.0" />
</ItemGroup>

<!-- Testing Microsoft.Extensions dependencies (non-net8.0) -->
<ItemGroup Condition="'$(TargetFramework)' != 'net8.0'">
<PackageVersion Include="Microsoft.Extensions.AI.OpenAI" Version="10.2.0-preview.1.26063.2" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="$(System10Version)" />
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="$(System10Version)" />
<PackageVersion Include="Microsoft.Extensions.Logging" Version="$(System10Version)" />
<PackageVersion Include="Microsoft.Extensions.Logging.Console" Version="$(System10Version)" />
<PackageVersion Include="Microsoft.Extensions.Options" Version="$(System10Version)" />
<PackageVersion Include="Microsoft.Extensions.TimeProvider.Testing" Version="10.1.0" />
</ItemGroup>

<!-- Testing Microsoft.Extensions dependencies (net8.0) -->
<ItemGroup Condition="'$(TargetFramework)' == 'net8.0'">
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="8.0.1" />
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="8.0.1" />
<PackageVersion Include="Microsoft.Extensions.Logging" Version="8.0.1" />
<PackageVersion Include="Microsoft.Extensions.Logging.Console" Version="8.0.1" />
<PackageVersion Include="Microsoft.Extensions.Options" Version="8.0.2" />
<PackageVersion Include="Microsoft.Extensions.TimeProvider.Testing" Version="8.1.0" />
</ItemGroup>
</Project>
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,13 @@ For more information about MCP:
- [Protocol Specification](https://modelcontextprotocol.io/specification/)
- [GitHub Organization](https://github.com/modelcontextprotocol)

## Prerequisites

The SDK targets .NET Standard 2.0, .NET 8.0, .NET 9.0, and .NET 10.0. When targeting .NET 8.0, the SDK uses 8.x versions of `Microsoft.Extensions.*` dependencies for runtime compatibility.

> [!IMPORTANT]
> The SDK depends on `System.Text.Json` 10.x on all target frameworks, including .NET 8.0. If your application pins `System.Text.Json` to an 8.x version, you will need to allow the upgrade to 10.x for the SDK to function correctly.

## Installation

To get started, install the package from NuGet
Expand Down
2 changes: 1 addition & 1 deletion samples/AspNetCoreMcpServer/AspNetCoreMcpServer.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<PublishAot>true</PublishAot>
Expand Down
2 changes: 1 addition & 1 deletion samples/ChatWithTools/ChatWithTools.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<!--
Expand Down
4 changes: 4 additions & 0 deletions src/ModelContextProtocol.Core/Diagnostics.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ internal static class Diagnostics
internal static Meter Meter { get; } = new("Experimental.ModelContextProtocol");

internal static Histogram<double> CreateDurationHistogram(string name, string description) =>
#if NET8_0
Meter.CreateHistogram<double>(name, "s", description);
#else
Meter.CreateHistogram(name, "s", description, advice: ExplicitBucketBoundaries);

/// <summary>
Expand All @@ -24,6 +27,7 @@ internal static Histogram<double> CreateDurationHistogram(string name, string de
{
HistogramBucketBoundaries = [0.01, 0.02, 0.05, 0.1, 0.2, 0.5, 1, 2, 5, 10, 30, 60, 120, 300],
};
#endif

internal static ActivityContext ExtractActivityContext(this DistributedContextPropagator propagator, JsonRpcMessage message)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@
<PackageReference Include="System.Threading.Channels" />
</ItemGroup>

<!-- Dependencies only needed by net8.0 -->
<ItemGroup Condition="'$(TargetFramework)' == 'net8.0'">
<PackageReference Include="System.Text.Json" />
</ItemGroup>

<!-- Dependencies only needed by pre-net9.0 -->
<ItemGroup Condition="!$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', 'net9.0'))">
<PackageReference Include="System.IO.Pipelines" />
Expand Down
7 changes: 7 additions & 0 deletions src/ModelContextProtocol.Core/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,13 @@ For more information about MCP:
- [Protocol Specification](https://modelcontextprotocol.io/specification/)
- [GitHub Organization](https://github.com/modelcontextprotocol)

## Prerequisites

The SDK targets .NET Standard 2.0, .NET 8.0, .NET 9.0, and .NET 10.0. When targeting .NET 8.0, the SDK uses 8.x versions of `Microsoft.Extensions.*` dependencies for runtime compatibility.

> [!IMPORTANT]
> The SDK depends on `System.Text.Json` 10.x on all target frameworks, including .NET 8.0. If your application pins `System.Text.Json` to an 8.x version, you will need to allow the upgrade to 10.x for the SDK to function correctly.

## Installation

To get started, install the core package from NuGet
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ internal sealed partial class StreamableHttpPostTransport(
CancellationToken sessionCancellationToken,
ILogger logger) : ITransport
{
private readonly ILogger _logger = logger;
private readonly SemaphoreSlim _messageLock = new(1, 1);
private readonly TaskCompletionSource<bool> _httpResponseTcs = new(TaskCreationOptions.RunContinuationsAsynchronously);
private readonly SseEventWriter _httpSseWriter = new(responseStream);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" />
<PackageReference Include="Microsoft.Extensions.Caching.Memory" />
<PackageReference Include="Microsoft.Extensions.AI" />
<PackageReference Include="Microsoft.Extensions.AI.OpenAI" />
<PackageReference Include="Microsoft.Extensions.AI.OpenAI" Condition="'$(TargetFramework)' != 'net8.0'" />
<PackageReference Include="Microsoft.Extensions.Logging" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" />
<PackageReference Include="Microsoft.Extensions.TimeProvider.Testing" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -173,10 +173,19 @@ public async Task RunPendingConformanceTest_ServerSsePolling()
);
}

var stdOut = outputBuilder.ToString();
var stdErr = errorBuilder.ToString();

// On Windows, the Node.js conformance runner can crash during shutdown due to a libuv assertion
// (UV_HANDLE_CLOSING), producing a non-zero exit code even when all tests pass. Fall back to
// checking the output for "0 failed" when the exit code is non-zero.
bool success = process.ExitCode == 0 ||
(stdOut.Contains("0 failed") && !stdOut.Contains("FAILURE"));

return (
Success: process.ExitCode == 0,
Output: outputBuilder.ToString(),
Error: errorBuilder.ToString()
Success: success,
Output: stdOut,
Error: stdErr
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,9 @@ public ClientIntegrationTestFixture()

TestServerTransportOptions = new()
{
Command = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "TestServer.exe" : PlatformDetection.IsMonoRuntime ? "mono" : "dotnet",
Command = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? Path.Combine(AppContext.BaseDirectory, "TestServer.exe") : PlatformDetection.IsMonoRuntime ? "mono" : "dotnet",
Name = "TestServer",
WorkingDirectory = AppContext.BaseDirectory,
};

if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
Expand Down
6 changes: 6 additions & 0 deletions tests/ModelContextProtocol.Tests/ClientIntegrationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,21 @@
using ModelContextProtocol.Client;
using ModelContextProtocol.Protocol;
using ModelContextProtocol.Tests.Utils;
#if !NET8_0
using OpenAI;
#endif
using System.Text.Json;
using System.Text.Json.Serialization;

namespace ModelContextProtocol.Tests;

public partial class ClientIntegrationTests : LoggedTest, IClassFixture<ClientIntegrationTestFixture>
{
#if !NET8_0
private static readonly string? s_openAIKey = Environment.GetEnvironmentVariable("AI:OpenAI:ApiKey");

public static bool NoOpenAIKeySet => string.IsNullOrWhiteSpace(s_openAIKey);
#endif

private readonly ClientIntegrationTestFixture _fixture;

Expand Down Expand Up @@ -492,6 +496,7 @@ public async Task CallTool_Stdio_MemoryServer()
await client.DisposeAsync();
}

#if !NET8_0
[Fact(Skip = "Requires OpenAI API Key", SkipWhen = nameof(NoOpenAIKeySet))]
public async Task ListToolsAsync_UsingEverythingServer_ToolsAreProperlyCalled()
{
Expand Down Expand Up @@ -547,6 +552,7 @@ public async Task SamplingViaChatClient_RequestResponseProperlyPropagated()
Assert.Contains("LLM sampling result:", content.Text);
Assert.Contains("Eiffel", content.Text);
}
#endif

[Theory]
[MemberData(nameof(GetClients))]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.AI" />
<PackageReference Include="Microsoft.Extensions.AI.OpenAI" />
<PackageReference Include="Microsoft.Extensions.AI.OpenAI" Condition="'$(TargetFramework)' != 'net8.0'" />
<PackageReference Include="Microsoft.Extensions.Caching.Memory" />
<PackageReference Include="Microsoft.Extensions.Logging" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ public async Task EscapesCliArgumentsCorrectly(string? cliArgumentValue)
Command = (PlatformDetection.IsMonoRuntime, PlatformDetection.IsWindows) switch
{
(true, _) => "mono",
(_, true) => "TestServer.exe",
(_, true) => Path.Combine(AppContext.BaseDirectory, "TestServer.exe"),
_ => "dotnet",
},
Arguments = (PlatformDetection.IsMonoRuntime, PlatformDetection.IsWindows) switch
Expand All @@ -120,6 +120,7 @@ public async Task EscapesCliArgumentsCorrectly(string? cliArgumentValue)
(_, true) => [cliArgument],
_ => ["TestServer.dll", cliArgument],
},
WorkingDirectory = AppContext.BaseDirectory,
};

var transport = new StdioClientTransport(options, LoggerFactory);
Expand Down