1+ using Microsoft . Extensions . Logging ;
12using ModelContextProtocol . Client ;
23using ModelContextProtocol . Protocol . Messages ;
34using ModelContextProtocol . Protocol . Transport ;
45using ModelContextProtocol . Protocol . Types ;
6+ using Moq ;
57using System . Text . Json ;
68using System . Threading . Channels ;
79
@@ -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 : ITransport , IClientTransport
192+ [ Theory ]
193+ [ InlineData ( typeof ( NopTransport ) ) ]
194+ [ InlineData ( typeof ( FailureTransport ) ) ]
195+ public async Task CreateAsync_WithCapabilitiesOptions ( 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 , p , 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 : ITransport , IClientTransport
191254 {
192255 private readonly Channel < IJsonRpcMessage > _channel = Channel . CreateUnbounded < IJsonRpcMessage > ( ) ;
193256
@@ -199,7 +262,7 @@ private sealed class NopTransport : ITransport, IClientTransport
199262
200263 public ValueTask DisposeAsync ( ) => default ;
201264
202- public Task SendMessageAsync ( IJsonRpcMessage message , CancellationToken cancellationToken = default )
265+ public virtual Task SendMessageAsync ( IJsonRpcMessage message , CancellationToken cancellationToken = default )
203266 {
204267 switch ( message )
205268 {
@@ -224,4 +287,14 @@ public Task SendMessageAsync(IJsonRpcMessage message, CancellationToken cancella
224287 return Task . CompletedTask ;
225288 }
226289 }
290+
291+ private sealed class FailureTransport : NopTransport
292+ {
293+ public const string ExpectedMessage = "Something failed" ;
294+
295+ public override Task SendMessageAsync ( IJsonRpcMessage message , CancellationToken cancellationToken = default )
296+ {
297+ throw new InvalidOperationException ( ExpectedMessage ) ;
298+ }
299+ }
227300}
0 commit comments