Files
wehub-resource-sync db620d33df
dotnet-build-and-test / dotnet-test-functions (push) Has been cancelled
dotnet-build-and-test / paths-filter (push) Has been cancelled
dotnet-build-and-test / dotnet-build (Debug, windows-latest, net9.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-build (Release, ubuntu-latest, net10.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-build (Release, ubuntu-latest, net8.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-build (Release, windows-latest, net472) (push) Has been cancelled
dotnet-build-and-test / dotnet-test (Release, integration, true, ubuntu-latest, net10.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-test (Release, integration, true, windows-latest, net472) (push) Has been cancelled
dotnet-build-and-test / dotnet-foundry-hosted-it (push) Has been cancelled
dotnet-build-and-test / dotnet-build-and-test-check (push) Has been cancelled
dotnet-build-and-test / Integration Test Report (push) Has been cancelled
CodeQL / Analyze (csharp) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:39:25 +08:00

77 lines
2.8 KiB
C#

// Copyright (c) Microsoft. All rights reserved.
using System;
using System.Diagnostics.CodeAnalysis;
using System.IO.Pipelines;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using ModelContextProtocol.Client;
using ModelContextProtocol.Protocol;
using ModelContextProtocol.Server;
namespace Microsoft.Agents.AI.Skills.Mcp.UnitTests;
/// <summary>
/// Spins up an in-memory MCP server hosting a configurable set of resources, and returns an
/// <see cref="McpClient"/> connected to it. Disposes both ends together.
/// </summary>
[SuppressMessage("Performance", "CA1812:Avoid uninstantiated internal classes", Justification = "Instantiated by AgentMcpSkillsSourceTests which is temporarily excluded from compilation.")]
internal sealed class InMemoryMcpServer : IAsyncDisposable
{
private readonly Pipe _clientToServerPipe = new();
private readonly Pipe _serverToClientPipe = new();
private readonly CancellationTokenSource _cts = new();
private readonly ServiceProvider _serviceProvider;
private readonly Task _serverTask;
public InMemoryMcpServer(Action<IMcpServerBuilder> configure)
{
var services = new ServiceCollection();
services.AddLogging(builder => builder.AddProvider(NullLoggerProvider.Instance));
IMcpServerBuilder builder = services
.AddMcpServer()
.WithStreamServerTransport(
inputStream: this._clientToServerPipe.Reader.AsStream(),
outputStream: this._serverToClientPipe.Writer.AsStream());
configure(builder);
this._serviceProvider = services.BuildServiceProvider();
var server = this._serviceProvider.GetRequiredService<McpServer>();
this._serverTask = server.RunAsync(this._cts.Token);
}
public async Task<McpClient> CreateClientAsync(CancellationToken cancellationToken = default)
{
return await McpClient.CreateAsync(
new StreamClientTransport(
serverInput: this._clientToServerPipe.Writer.AsStream(),
serverOutput: this._serverToClientPipe.Reader.AsStream()),
cancellationToken: cancellationToken).ConfigureAwait(false);
}
public async ValueTask DisposeAsync()
{
await this._cts.CancelAsync().ConfigureAwait(false);
this._clientToServerPipe.Writer.Complete();
this._serverToClientPipe.Writer.Complete();
try
{
await this._serverTask.ConfigureAwait(false);
}
catch (OperationCanceledException)
{
// Expected when the server is cancelled during shutdown.
}
await this._serviceProvider.DisposeAsync().ConfigureAwait(false);
this._cts.Dispose();
}
}