1
1
using Microsoft . Extensions . AI ;
2
2
using Microsoft . Extensions . DependencyInjection ;
3
3
using ModelContextProtocol . Protocol . Messages ;
4
- using ModelContextProtocol . Protocol . Transport ;
5
4
using ModelContextProtocol . Protocol . Types ;
6
5
using ModelContextProtocol . Server ;
7
6
using ModelContextProtocol . Tests . Utils ;
8
- using Moq ;
9
7
using System . Reflection ;
10
8
11
9
namespace ModelContextProtocol . Tests . Server ;
12
10
13
11
public class McpServerTests : LoggedTest
14
12
{
15
- private readonly Mock < ITransport > _serverTransport ;
16
13
private readonly McpServerOptions _options ;
17
- private readonly IServiceProvider _serviceProvider ;
18
14
19
15
public McpServerTests ( ITestOutputHelper testOutputHelper )
20
16
: base ( testOutputHelper )
21
17
{
22
- _serverTransport = new Mock < ITransport > ( ) ;
23
18
_options = CreateOptions ( ) ;
24
- _serviceProvider = new ServiceCollection ( ) . BuildServiceProvider ( ) ;
25
19
}
26
20
27
21
private static McpServerOptions CreateOptions ( ServerCapabilities ? capabilities = null )
@@ -39,7 +33,8 @@ private static McpServerOptions CreateOptions(ServerCapabilities? capabilities =
39
33
public async Task Constructor_Should_Initialize_With_Valid_Parameters ( )
40
34
{
41
35
// 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 ) ;
43
38
44
39
// Assert
45
40
Assert . NotNull ( server ) ;
@@ -49,21 +44,23 @@ public async Task Constructor_Should_Initialize_With_Valid_Parameters()
49
44
public void Constructor_Throws_For_Null_Transport ( )
50
45
{
51
46
// Arrange, Act & Assert
52
- Assert . Throws < ArgumentNullException > ( ( ) => McpServerFactory . Create ( null ! , _options , LoggerFactory , _serviceProvider ) ) ;
47
+ Assert . Throws < ArgumentNullException > ( ( ) => McpServerFactory . Create ( null ! , _options , LoggerFactory ) ) ;
53
48
}
54
49
55
50
[ Fact ]
56
- public void Constructor_Throws_For_Null_Options ( )
51
+ public async Task Constructor_Throws_For_Null_Options ( )
57
52
{
58
53
// 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 ) ) ;
60
56
}
61
57
62
58
[ Fact ]
63
59
public async Task Constructor_Does_Not_Throw_For_Null_Logger ( )
64
60
{
65
61
// 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 ) ;
67
64
68
65
// Assert
69
66
Assert . NotNull ( server ) ;
@@ -73,7 +70,8 @@ public async Task Constructor_Does_Not_Throw_For_Null_Logger()
73
70
public async Task Constructor_Does_Not_Throw_For_Null_ServiceProvider ( )
74
71
{
75
72
// 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 ) ;
77
75
78
76
// Assert
79
77
Assert . NotNull ( server ) ;
@@ -83,27 +81,23 @@ public async Task Constructor_Does_Not_Throw_For_Null_ServiceProvider()
83
81
public async Task RunAsync_Should_Throw_InvalidOperationException_If_Already_Running ( )
84
82
{
85
83
// 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 ) ;
87
86
var runTask = server . RunAsync ( TestContext . Current . CancellationToken ) ;
88
87
89
88
// Act & Assert
90
89
await Assert . ThrowsAsync < InvalidOperationException > ( ( ) => server . RunAsync ( TestContext . Current . CancellationToken ) ) ;
91
90
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 ;
100
93
}
101
94
102
95
[ Fact ]
103
96
public async Task RequestSamplingAsync_Should_Throw_McpServerException_If_Client_Does_Not_Support_Sampling ( )
104
97
{
105
98
// 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 ) ;
107
101
SetClientCapabilities ( server , new ClientCapabilities ( ) ) ;
108
102
109
103
var action = ( ) => server . RequestSamplingAsync ( new CreateMessageRequestParams { Messages = [ ] } , CancellationToken . None ) ;
@@ -117,7 +111,7 @@ public async Task RequestSamplingAsync_Should_SendRequest()
117
111
{
118
112
// Arrange
119
113
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 ) ;
121
115
SetClientCapabilities ( server , new ClientCapabilities { Sampling = new SamplingCapability ( ) } ) ;
122
116
123
117
var runTask = server . RunAsync ( TestContext . Current . CancellationToken ) ;
@@ -138,7 +132,8 @@ public async Task RequestSamplingAsync_Should_SendRequest()
138
132
public async Task RequestRootsAsync_Should_Throw_McpServerException_If_Client_Does_Not_Support_Roots ( )
139
133
{
140
134
// 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 ) ;
142
137
SetClientCapabilities ( server , new ClientCapabilities ( ) ) ;
143
138
144
139
// Act & Assert
@@ -150,7 +145,7 @@ public async Task RequestRootsAsync_Should_SendRequest()
150
145
{
151
146
// Arrange
152
147
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 ) ;
154
149
SetClientCapabilities ( server , new ClientCapabilities { Roots = new RootsCapability ( ) } ) ;
155
150
var runTask = server . RunAsync ( TestContext . Current . CancellationToken ) ;
156
151
@@ -507,7 +502,7 @@ private async Task Can_Handle_Requests(ServerCapabilities? serverCapabilities, s
507
502
var options = CreateOptions ( serverCapabilities ) ;
508
503
configureOptions ? . Invoke ( options ) ;
509
504
510
- await using var server = McpServerFactory . Create ( transport , options , LoggerFactory , _serviceProvider ) ;
505
+ await using var server = McpServerFactory . Create ( transport , options , LoggerFactory ) ;
511
506
512
507
var runTask = server . RunAsync ( TestContext . Current . CancellationToken ) ;
513
508
@@ -542,7 +537,7 @@ private async Task Throws_Exception_If_No_Handler_Assigned(ServerCapabilities se
542
537
await using var transport = new TestServerTransport ( ) ;
543
538
var options = CreateOptions ( serverCapabilities ) ;
544
539
545
- Assert . Throws < McpServerException > ( ( ) => McpServerFactory . Create ( transport , options , LoggerFactory , _serviceProvider ) ) ;
540
+ Assert . Throws < McpServerException > ( ( ) => McpServerFactory . Create ( transport , options , LoggerFactory ) ) ;
546
541
}
547
542
548
543
[ Fact ]
@@ -553,7 +548,6 @@ public async Task AsSamplingChatClient_NoSamplingSupport_Throws()
553
548
Assert . Throws < ArgumentException > ( "server" , ( ) => server . AsSamplingChatClient ( ) ) ;
554
549
}
555
550
556
-
557
551
[ Fact ]
558
552
public async Task AsSamplingChatClient_HandlesRequestResponse ( )
559
553
{
@@ -583,6 +577,26 @@ public async Task AsSamplingChatClient_HandlesRequestResponse()
583
577
Assert . Equal ( ChatRole . Assistant , response . Messages [ 0 ] . Role ) ;
584
578
}
585
579
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
+
586
600
private static void SetClientCapabilities ( IMcpServer server , ClientCapabilities capabilities )
587
601
{
588
602
PropertyInfo ? property = server . GetType ( ) . GetProperty ( "ClientCapabilities" , BindingFlags . Public | BindingFlags . Instance ) ;
@@ -644,7 +658,7 @@ public async Task NotifyProgress_Should_Be_Handled()
644
658
645
659
var notificationReceived = new TaskCompletionSource < JsonRpcNotification > ( ) ;
646
660
647
- var server = McpServerFactory . Create ( transport , options , LoggerFactory , _serviceProvider ) ;
661
+ var server = McpServerFactory . Create ( transport , options , LoggerFactory ) ;
648
662
server . AddNotificationHandler ( NotificationMethods . ProgressNotification , notification =>
649
663
{
650
664
notificationReceived . SetResult ( notification ) ;
0 commit comments