Reduce dependencies to 8.x versions of packages (#195)

* Reduce dependencies to 8.x versions of packages

When building for netstandard2.0 or net8.0, the ModelContextProtocol library now only depends on 8.x versions of nuget packages, rather than using the newer 9.x versions. The exception to this are packages that don't have 8.x versions.

This also introduces a net9.0 tfm. When building for net9.0, it'll use 9.x versions of packages.

The PR temporarily removes the Microsoft.Extensions.AI.nupkg reference (the Microsoft.Extensions.AI.Abstractions reference is still very much there). The latest Microsoft.Extensions.AI package on nuget has a System.Text.Json 9.x reference, but the next build to be published drops that down to 8.x as well. Once that build is published, the dependency will be added back, and temporary polyfills (in particular TemporaryAIFunctionFactory) will be deleted.

* Fix StdioClientSessionTransport shutdown

StreamClientSessionTransport's ReadMessageAsync method does await CleanupAsync in its finally. CleanupAsync awaits _readTask. _readTask _is_ ReadMessageAsync. So it's waiting for itself to complete, which will then always timeout. We may want to refactor how this works more in the future, but for now, make sure it doesn't wait for itself.

* Fix dangling requests when server unexpectedly shuts down

If the client makes a request to the server and while waiting for the response the server shuts down, the client hangs. Make sure that when the client's message loops ends due to the server's output it's reading from ends, any pending requests are notified.

* Add net9.0 testing

* Avoid docker port conflicts from test suites running concurrently
This commit is contained in:
Stephen Toub 2025-04-03 14:01:46 -04:00 • committed by GitHub
commit c452dc8e26
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 247 additions and 91 deletions

View file

@ -53,7 +53,7 @@ public sealed class SseResponseStreamTransport(Stream sseResponseStream, string
return;
}
JsonSerializer.Serialize(GetUtf8JsonWriter(writer), item.Data, McpJsonUtilities.JsonContext.Default.IJsonRpcMessage);
JsonSerializer.Serialize(GetUtf8JsonWriter(writer), item.Data, McpJsonUtilities.JsonContext.Default.IJsonRpcMessage!);
}
/// <inheritdoc/>

View file

@ -29,9 +29,16 @@ internal class StreamClientSessionTransport : TransportBase
_serverInput = serverInput;
EndpointName = endpointName;
// Start reading messages in the background
// Start reading messages in the background. We use the rarer pattern of new Task + Start
// in order to ensure that the body of the task will always see _readTask initialized.
// It is then able to reliably null it out on completion.
Logger.TransportReadingMessages(endpointName);
_readTask = Task.Run(() => ReadMessagesAsync(_shutdownCts.Token), CancellationToken.None);
var readTask = new Task<Task>(
thisRef => ((StreamClientSessionTransport)thisRef!).ReadMessagesAsync(_shutdownCts.Token),
this,
TaskCreationOptions.DenyChildAttach);
_readTask = readTask.Unwrap();
readTask.Start();
SetConnected(true);
}
@ -117,6 +124,7 @@ internal class StreamClientSessionTransport : TransportBase
}
finally
{
_readTask = null;
await CleanupAsync(cancellationToken).ConfigureAwait(false);
}
}

View file

@ -6,7 +6,7 @@ namespace ModelContextProtocol.Protocol.Types;
/// A request to include context from one or more MCP servers (including the caller), to be attached to the prompt.
/// <see href="https://github.com/modelcontextprotocol/specification/blob/main/schema/">See the schema for details</see>
/// </summary>
[JsonConverter(typeof(JsonStringEnumConverter<ContextInclusion>))]
[JsonConverter(typeof(CustomizableJsonStringEnumConverter<ContextInclusion>))]
public enum ContextInclusion
{
/// <summary>

View file

@ -7,7 +7,7 @@ namespace ModelContextProtocol.Protocol.Types;
/// These map to syslog message severities, as specified in RFC-5424:
/// https://datatracker.ietf.org/doc/html/rfc5424#section-6.2.1
/// </summary>
[JsonConverter(typeof(JsonStringEnumConverter<LoggingLevel>))]
[JsonConverter(typeof(CustomizableJsonStringEnumConverter<LoggingLevel>))]
public enum LoggingLevel
{
/// <summary>Detailed debug information, typically only valuable to developers.</summary>

View file

@ -6,7 +6,7 @@ namespace ModelContextProtocol.Protocol.Types;
/// Represents the type of role in the conversation.
/// <see href="https://github.com/modelcontextprotocol/specification/blob/main/schema/">See the schema for details</see>
/// </summary>
[JsonConverter(typeof(JsonStringEnumConverter<Role>))]
[JsonConverter(typeof(CustomizableJsonStringEnumConverter<Role>))]
public enum Role
{
/// <summary>