11using Microsoft . Extensions . AI ;
22using Microsoft . Extensions . DependencyInjection ;
33using ModelContextProtocol . Protocol . Messages ;
4- using ModelContextProtocol . Protocol . Transport ;
54using ModelContextProtocol . Protocol . Types ;
65using ModelContextProtocol . Server ;
76using ModelContextProtocol . Tests . Utils ;
8- using Moq ;
97using System . Reflection ;
108
119namespace ModelContextProtocol . Tests . Server ;
1210
1311public class McpServerTests : LoggedTest
1412{
15- private readonly Mock < ITransport > _serverTransport ;
1613 private readonly McpServerOptions _options ;
17- private readonly IServiceProvider _serviceProvider ;
1814
1915 public McpServerTests ( ITestOutputHelper testOutputHelper )
2016 : base ( testOutputHelper )
2117 {
22- _serverTransport = new Mock < ITransport > ( ) ;
2318 _options = CreateOptions ( ) ;
24- _serviceProvider = new ServiceCollection ( ) . BuildServiceProvider ( ) ;
2519 }
2620
2721 private static McpServerOptions CreateOptions ( ServerCapabilities ? capabilities = null )
@@ -39,7 +33,8 @@ private static McpServerOptions CreateOptions(ServerCapabilities? capabilities =
3933 public async Task Constructor_Should_Initialize_With_Valid_Parameters ( )
4034 {
4135 // Arrange & Act
42- await using var server = McpServerFactory . Create ( _serverTransport . Object , _options , LoggerFactory , _serviceProvider ) ;
36+ await using var transport = new TestServerTransport ( ) ;
37+ await using var server = McpServerFactory . Create ( transport , _options , LoggerFactory ) ;
4338
4439 // Assert
4540 Assert . NotNull ( server ) ;
@@ -49,21 +44,23 @@ public async Task Constructor_Should_Initialize_With_Valid_Parameters()
4944 public void Constructor_Throws_For_Null_Transport ( )
5045 {
5146 // Arrange, Act & Assert
52- Assert . Throws < ArgumentNullException > ( ( ) => McpServerFactory . Create ( null ! , _options , LoggerFactory , _serviceProvider ) ) ;
47+ Assert . Throws < ArgumentNullException > ( ( ) => McpServerFactory . Create ( null ! , _options , LoggerFactory ) ) ;
5348 }
5449
5550 [ Fact ]
56- public void Constructor_Throws_For_Null_Options ( )
51+ public async Task Constructor_Throws_For_Null_Options ( )
5752 {
5853 // Arrange, Act & Assert
59- Assert . Throws < ArgumentNullException > ( ( ) => McpServerFactory . Create ( _serverTransport . Object , null ! , LoggerFactory , _serviceProvider ) ) ;
54+ await using var transport = new TestServerTransport ( ) ;
55+ Assert . Throws < ArgumentNullException > ( ( ) => McpServerFactory . Create ( transport , null ! , LoggerFactory ) ) ;
6056 }
6157
6258 [ Fact ]
6359 public async Task Constructor_Does_Not_Throw_For_Null_Logger ( )
6460 {
6561 // Arrange & Act
66- await using var server = McpServerFactory . Create ( _serverTransport . Object , _options , null , _serviceProvider ) ;
62+ await using var transport = new TestServerTransport ( ) ;
63+ await using var server = McpServerFactory . Create ( transport , _options , null ) ;
6764
6865 // Assert
6966 Assert . NotNull ( server ) ;
@@ -73,7 +70,8 @@ public async Task Constructor_Does_Not_Throw_For_Null_Logger()
7370 public async Task Constructor_Does_Not_Throw_For_Null_ServiceProvider ( )
7471 {
7572 // Arrange & Act
76- await using var server = McpServerFactory . Create ( _serverTransport . Object , _options , LoggerFactory , null ) ;
73+ await using var transport = new TestServerTransport ( ) ;
74+ await using var server = McpServerFactory . Create ( transport , _options , LoggerFactory , null ) ;
7775
7876 // Assert
7977 Assert . NotNull ( server ) ;
@@ -83,27 +81,23 @@ public async Task Constructor_Does_Not_Throw_For_Null_ServiceProvider()
8381 public async Task RunAsync_Should_Throw_InvalidOperationException_If_Already_Running ( )
8482 {
8583 // Arrange
86- await using var server = McpServerFactory . Create ( _serverTransport . Object , _options , LoggerFactory , _serviceProvider ) ;
84+ await using var transport = new TestServerTransport ( ) ;
85+ await using var server = McpServerFactory . Create ( transport , _options , LoggerFactory ) ;
8786 var runTask = server . RunAsync ( TestContext . Current . CancellationToken ) ;
8887
8988 // Act & Assert
9089 await Assert . ThrowsAsync < InvalidOperationException > ( ( ) => server . RunAsync ( TestContext . Current . CancellationToken ) ) ;
9190
92- try
93- {
94- await runTask ;
95- }
96- catch ( NullReferenceException )
97- {
98- // _serverTransport.Object returns a null MessageReader
99- }
91+ await transport . DisposeAsync ( ) ;
92+ await runTask ;
10093 }
10194
10295 [ Fact ]
10396 public async Task RequestSamplingAsync_Should_Throw_McpServerException_If_Client_Does_Not_Support_Sampling ( )
10497 {
10598 // Arrange
106- await using var server = McpServerFactory . Create ( _serverTransport . Object , _options , LoggerFactory , _serviceProvider ) ;
99+ await using var transport = new TestServerTransport ( ) ;
100+ await using var server = McpServerFactory . Create ( transport , _options , LoggerFactory ) ;
107101 SetClientCapabilities ( server , new ClientCapabilities ( ) ) ;
108102
109103 var action = ( ) => server . RequestSamplingAsync ( new CreateMessageRequestParams { Messages = [ ] } , CancellationToken . None ) ;
@@ -117,7 +111,7 @@ public async Task RequestSamplingAsync_Should_SendRequest()
117111 {
118112 // Arrange
119113 await using var transport = new TestServerTransport ( ) ;
120- await using var server = McpServerFactory . Create ( transport , _options , LoggerFactory , _serviceProvider ) ;
114+ await using var server = McpServerFactory . Create ( transport , _options , LoggerFactory ) ;
121115 SetClientCapabilities ( server , new ClientCapabilities { Sampling = new SamplingCapability ( ) } ) ;
122116
123117 var runTask = server . RunAsync ( TestContext . Current . CancellationToken ) ;
@@ -138,7 +132,8 @@ public async Task RequestSamplingAsync_Should_SendRequest()
138132 public async Task RequestRootsAsync_Should_Throw_McpServerException_If_Client_Does_Not_Support_Roots ( )
139133 {
140134 // Arrange
141- await using var server = McpServerFactory . Create ( _serverTransport . Object , _options , LoggerFactory , _serviceProvider ) ;
135+ await using var transport = new TestServerTransport ( ) ;
136+ await using var server = McpServerFactory . Create ( transport , _options , LoggerFactory ) ;
142137 SetClientCapabilities ( server , new ClientCapabilities ( ) ) ;
143138
144139 // Act & Assert
@@ -150,7 +145,7 @@ public async Task RequestRootsAsync_Should_SendRequest()
150145 {
151146 // Arrange
152147 await using var transport = new TestServerTransport ( ) ;
153- await using var server = McpServerFactory . Create ( transport , _options , LoggerFactory , _serviceProvider ) ;
148+ await using var server = McpServerFactory . Create ( transport , _options , LoggerFactory ) ;
154149 SetClientCapabilities ( server , new ClientCapabilities { Roots = new RootsCapability ( ) } ) ;
155150 var runTask = server . RunAsync ( TestContext . Current . CancellationToken ) ;
156151
@@ -507,7 +502,7 @@ private async Task Can_Handle_Requests(ServerCapabilities? serverCapabilities, s
507502 var options = CreateOptions ( serverCapabilities ) ;
508503 configureOptions ? . Invoke ( options ) ;
509504
510- await using var server = McpServerFactory . Create ( transport , options , LoggerFactory , _serviceProvider ) ;
505+ await using var server = McpServerFactory . Create ( transport , options , LoggerFactory ) ;
511506
512507 var runTask = server . RunAsync ( TestContext . Current . CancellationToken ) ;
513508
@@ -542,7 +537,7 @@ private async Task Throws_Exception_If_No_Handler_Assigned(ServerCapabilities se
542537 await using var transport = new TestServerTransport ( ) ;
543538 var options = CreateOptions ( serverCapabilities ) ;
544539
545- Assert . Throws < McpServerException > ( ( ) => McpServerFactory . Create ( transport , options , LoggerFactory , _serviceProvider ) ) ;
540+ Assert . Throws < McpServerException > ( ( ) => McpServerFactory . Create ( transport , options , LoggerFactory ) ) ;
546541 }
547542
548543 [ Fact ]
@@ -553,7 +548,6 @@ public async Task AsSamplingChatClient_NoSamplingSupport_Throws()
553548 Assert . Throws < ArgumentException > ( "server" , ( ) => server . AsSamplingChatClient ( ) ) ;
554549 }
555550
556-
557551 [ Fact ]
558552 public async Task AsSamplingChatClient_HandlesRequestResponse ( )
559553 {
@@ -583,6 +577,26 @@ public async Task AsSamplingChatClient_HandlesRequestResponse()
583577 Assert . Equal ( ChatRole . Assistant , response . Messages [ 0 ] . Role ) ;
584578 }
585579
580+ [ Fact ]
581+ public async Task Can_SendMessage_Before_RunAsync ( )
582+ {
583+ await using var transport = new TestServerTransport ( ) ;
584+ await using var server = McpServerFactory . Create ( transport , _options , LoggerFactory ) ;
585+
586+ var logNotification = new JsonRpcNotification ( )
587+ {
588+ Method = NotificationMethods . LoggingMessageNotification
589+ } ;
590+ await server . SendMessageAsync ( logNotification , TestContext . Current . CancellationToken ) ;
591+
592+ var runTask = server . RunAsync ( TestContext . Current . CancellationToken ) ;
593+ await transport . DisposeAsync ( ) ;
594+ await runTask ;
595+
596+ Assert . NotEmpty ( transport . SentMessages ) ;
597+ Assert . Same ( logNotification , transport . SentMessages [ 0 ] ) ;
598+ }
599+
586600 private static void SetClientCapabilities ( IMcpServer server , ClientCapabilities capabilities )
587601 {
588602 PropertyInfo ? property = server . GetType ( ) . GetProperty ( "ClientCapabilities" , BindingFlags . Public | BindingFlags . Instance ) ;
@@ -644,7 +658,7 @@ public async Task NotifyProgress_Should_Be_Handled()
644658
645659 var notificationReceived = new TaskCompletionSource < JsonRpcNotification > ( ) ;
646660
647- var server = McpServerFactory . Create ( transport , options , LoggerFactory , _serviceProvider ) ;
661+ var server = McpServerFactory . Create ( transport , options , LoggerFactory ) ;
648662 server . AddNotificationHandler ( NotificationMethods . ProgressNotification , notification =>
649663 {
650664 notificationReceived . SetResult ( notification ) ;
0 commit comments