1
+ using Microsoft . Extensions . Logging ;
1
2
using ModelContextProtocol . Client ;
2
3
using ModelContextProtocol . Protocol . Messages ;
3
4
using ModelContextProtocol . Protocol . Transport ;
4
5
using ModelContextProtocol . Protocol . Types ;
6
+ using Moq ;
5
7
using System . Text . Json ;
6
8
using System . Threading . Channels ;
7
9
@@ -187,7 +189,68 @@ public async Task McpFactory_WithInvalidTransportOptions_ThrowsFormatException(s
187
189
await Assert . ThrowsAsync < ArgumentException > ( ( ) => McpClientFactory . CreateAsync ( config , _defaultOptions , cancellationToken : TestContext . Current . CancellationToken ) ) ;
188
190
}
189
191
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
191
254
{
192
255
private readonly Channel < IJsonRpcMessage > _channel = Channel . CreateUnbounded < IJsonRpcMessage > ( ) ;
193
256
@@ -199,7 +262,7 @@ private sealed class NopTransport : ITransport, IClientTransport
199
262
200
263
public ValueTask DisposeAsync ( ) => default ;
201
264
202
- public Task SendMessageAsync ( IJsonRpcMessage message , CancellationToken cancellationToken = default )
265
+ public virtual Task SendMessageAsync ( IJsonRpcMessage message , CancellationToken cancellationToken = default )
203
266
{
204
267
switch ( message )
205
268
{
@@ -224,4 +287,14 @@ public Task SendMessageAsync(IJsonRpcMessage message, CancellationToken cancella
224
287
return Task . CompletedTask ;
225
288
}
226
289
}
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
+ }
227
300
}
0 commit comments