11using System . Threading . Channels ;
2+ using Microsoft . Extensions . Logging ;
23using ModelContextProtocol . Client ;
34using ModelContextProtocol . Protocol . Messages ;
45using ModelContextProtocol . Protocol . Transport ;
56using ModelContextProtocol . Protocol . Types ;
7+ using Moq ;
68
79namespace ModelContextProtocol . Tests . Client ;
810
@@ -186,7 +188,67 @@ public async Task McpFactory_WithInvalidTransportOptions_ThrowsFormatException(s
186188 await Assert . ThrowsAsync < ArgumentException > ( ( ) => McpClientFactory . CreateAsync ( config , _defaultOptions , cancellationToken : TestContext . Current . CancellationToken ) ) ;
187189 }
188190
189- private sealed class NopTransport : ITransport , IClientTransport
191+ [ Theory ]
192+ [ InlineData ( typeof ( NopTransport ) ) ]
193+ [ InlineData ( typeof ( FailureTransport ) ) ]
194+ public async Task CreateAsync_WithCapabilities ( Type transportType )
195+ {
196+ // Arrange
197+ var serverConfig = new McpServerConfig
198+ {
199+ Id = "TestServer" ,
200+ Name = "TestServer" ,
201+ TransportType = "stdio" ,
202+ Location = "test-location"
203+ } ;
204+
205+ var clientOptions = new McpClientOptions
206+ {
207+ ClientInfo = new Implementation
208+ {
209+ Name = "TestClient" ,
210+ Version = "1.0.0.0"
211+ } ,
212+ Capabilities = new ClientCapabilities
213+ {
214+ Sampling = new SamplingCapability
215+ {
216+ SamplingHandler = ( c , t ) => Task . FromResult (
217+ new CreateMessageResult {
218+ Content = new Content { Text = "result" } ,
219+ Model = "test-model" ,
220+ Role = "test-role" ,
221+ StopReason = "endTurn"
222+ } ) ,
223+ } ,
224+ Roots = new RootsCapability
225+ {
226+ ListChanged = true ,
227+ RootsHandler = ( t , r ) => Task . FromResult ( new ListRootsResult { Roots = [ ] } ) ,
228+ }
229+ }
230+ } ;
231+
232+ var clientTransport = ( IClientTransport ? ) Activator . CreateInstance ( transportType ) ;
233+ IMcpClient ? client = null ;
234+
235+ var actionTask = McpClientFactory . CreateAsync ( serverConfig , clientOptions , ( config , logger ) => clientTransport ?? new NopTransport ( ) , new Mock < ILoggerFactory > ( ) . Object , CancellationToken . None ) ;
236+
237+ // Act
238+ if ( clientTransport is FailureTransport )
239+ {
240+ var exception = await Assert . ThrowsAsync < InvalidOperationException > ( async ( ) => await actionTask ) ;
241+ Assert . Equal ( FailureTransport . ExpectedMessage , exception . Message ) ;
242+ }
243+ else
244+ {
245+ client = await actionTask ;
246+
247+ // Assert
248+ Assert . NotNull ( client ) ;
249+ }
250+ }
251+ private class NopTransport : ITransport , IClientTransport
190252 {
191253 private readonly Channel < IJsonRpcMessage > _channel = Channel . CreateUnbounded < IJsonRpcMessage > ( ) ;
192254
@@ -198,7 +260,7 @@ private sealed class NopTransport : ITransport, IClientTransport
198260
199261 public ValueTask DisposeAsync ( ) => default ;
200262
201- public Task SendMessageAsync ( IJsonRpcMessage message , CancellationToken cancellationToken = default )
263+ public virtual Task SendMessageAsync ( IJsonRpcMessage message , CancellationToken cancellationToken = default )
202264 {
203265 switch ( message )
204266 {
@@ -223,4 +285,14 @@ public Task SendMessageAsync(IJsonRpcMessage message, CancellationToken cancella
223285 return Task . CompletedTask ;
224286 }
225287 }
288+
289+ private sealed class FailureTransport : NopTransport
290+ {
291+ public const string ExpectedMessage = "Something failed" ;
292+
293+ public override Task SendMessageAsync ( IJsonRpcMessage message , CancellationToken cancellationToken = default )
294+ {
295+ throw new InvalidOperationException ( ExpectedMessage ) ;
296+ }
297+ }
226298}
0 commit comments