using System.Threading.Channels;
using Microsoft.Extensions.Logging;
using McpDotNet.Logging;
using McpDotNet.Protocol.Messages;
using Microsoft.Extensions.Logging.Abstractions;
namespace McpDotNet.Protocol.Transport;
///
/// Base class for implementing MCP transports with common functionality.
///
public abstract class TransportBase : ITransport
{
private readonly Channel _messageChannel;
private readonly ILogger _logger;
private bool _isConnected;
///
/// Initializes a new instance of the class.
///
protected TransportBase(ILoggerFactory? loggerFactory)
{
// Unbounded channel to prevent blocking on writes
_messageChannel = Channel.CreateUnbounded(new UnboundedChannelOptions
{
SingleReader = true,
SingleWriter = true,
});
_logger = (ILogger?)loggerFactory?.CreateLogger() ?? NullLogger.Instance;
}
///
public bool IsConnected => _isConnected;
///
public ChannelReader MessageReader => _messageChannel.Reader;
///
public abstract Task SendMessageAsync(IJsonRpcMessage message, CancellationToken cancellationToken = default);
///
public abstract ValueTask DisposeAsync();
///
/// Writes a message to the message channel.
///
/// The message to write.
/// Token to cancel the operation.
protected async Task WriteMessageAsync(IJsonRpcMessage message, CancellationToken cancellationToken = default)
{
if (!_isConnected)
{
throw new McpTransportException("Transport is not connected");
}
_logger.TransportWritingMessageToChannel(message);
await _messageChannel.Writer.WriteAsync(message, cancellationToken).ConfigureAwait(false);
_logger.TransportMessageWrittenToChannel();
}
///
/// Sets the connected state of the transport.
///
/// Whether the transport is connected.
protected void SetConnected(bool isConnected)
{
if (_isConnected == isConnected)
{
return;
}
_isConnected = isConnected;
if (!isConnected)
{
_messageChannel.Writer.Complete();
}
}
}