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(); } } }