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