File: Mcp\TestMcpServerTransport.cs
Web Access
Project: src\tests\Aspire.Cli.Tests\Aspire.Cli.Tests.csproj (Aspire.Cli.Tests)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.IO.Pipelines;
using Aspire.Cli.Mcp;
using Microsoft.Extensions.Logging;
using ModelContextProtocol.Client;
using ModelContextProtocol.Protocol;
using ModelContextProtocol.Server;
 
namespace Aspire.Cli.Tests.Mcp;
 
/// <summary>
/// A test helper that creates in-memory pipe-based transports for testing the MCP server.
/// Provides both the server transport (for DI injection) and a way to create a connected client.
/// Implements <see cref="IMcpTransportFactory"/> so it can be registered in DI.
/// </summary>
internal sealed class TestMcpServerTransport : IMcpTransportFactory, IDisposable
{
    private readonly ILoggerFactory? _loggerFactory;
 
    /// <summary>
    /// The pipe for sending data from client to server.
    /// </summary>
    public Pipe ClientToServerPipe { get; } = new();
 
    /// <summary>
    /// The pipe for sending data from server to client.
    /// </summary>
    public Pipe ServerToClientPipe { get; } = new();
 
    /// <summary>
    /// The server transport that should be registered in DI.
    /// </summary>
    public ITransport ServerTransport { get; }
 
    public TestMcpServerTransport(ILoggerFactory? loggerFactory = null)
    {
        _loggerFactory = loggerFactory;
        ServerTransport = new StreamServerTransport(
            ClientToServerPipe.Reader.AsStream(),
            ServerToClientPipe.Writer.AsStream(),
            serverName: "aspire-mcp-server",
            loggerFactory: _loggerFactory);
    }
 
    /// <inheritdoc />
    public ITransport CreateTransport() => ServerTransport;
 
    /// <summary>
    /// Creates an MCP client that connects to the server through the in-memory pipes.
    /// </summary>
    /// <param name="loggerFactory">Logger factory for the client.</param>
    /// <param name="cancellationToken">Cancellation token.</param>
    /// <returns>A connected MCP client.</returns>
    public Task<McpClient> CreateClientAsync(ILoggerFactory? loggerFactory = null, CancellationToken cancellationToken = default)
    {
        var clientTransport = new StreamClientTransport(
            serverInput: ClientToServerPipe.Writer.AsStream(),
            serverOutput: ServerToClientPipe.Reader.AsStream(),
            loggerFactory: loggerFactory);
 
        return McpClient.CreateAsync(clientTransport, loggerFactory: loggerFactory, cancellationToken: cancellationToken);
    }
 
    /// <summary>
    /// Completes the pipes to clean up resources.
    /// </summary>
    public void CompletePipes()
    {
        ClientToServerPipe.Reader.Complete();
        ClientToServerPipe.Writer.Complete();
        ServerToClientPipe.Reader.Complete();
        ServerToClientPipe.Writer.Complete();
    }
 
    public void Dispose()
    {
        CompletePipes();
    }
}