5
5
package io .modelcontextprotocol .client ;
6
6
7
7
import java .time .Duration ;
8
+ import java .util .List ;
8
9
import java .util .Map ;
9
10
import java .util .concurrent .atomic .AtomicBoolean ;
10
11
import java .util .concurrent .atomic .AtomicReference ;
11
12
import java .util .function .Consumer ;
12
13
import java .util .function .Function ;
13
14
14
15
import io .modelcontextprotocol .spec .McpClientTransport ;
15
- import io .modelcontextprotocol .spec .McpError ;
16
16
import io .modelcontextprotocol .spec .McpSchema ;
17
17
import io .modelcontextprotocol .spec .McpSchema .CallToolRequest ;
18
18
import io .modelcontextprotocol .spec .McpSchema .CallToolResult ;
@@ -112,33 +112,18 @@ void tearDown() {
112
112
113
113
static final Object DUMMY_RETURN_VALUE = new Object ();
114
114
115
- <T > void verifyNotificationTimesOut (Consumer <McpSyncClient > operation , String action ) {
116
- verifyCallTimesOut (client -> {
115
+ <T > void verifyNotificationSucceedsWithImplicitInitialization (Consumer <McpSyncClient > operation , String action ) {
116
+ verifyCallSucceedsWithImplicitInitialization (client -> {
117
117
operation .accept (client );
118
118
return DUMMY_RETURN_VALUE ;
119
119
}, action );
120
120
}
121
121
122
- <T > void verifyCallTimesOut (Function <McpSyncClient , T > blockingOperation , String action ) {
122
+ <T > void verifyCallSucceedsWithImplicitInitialization (Function <McpSyncClient , T > blockingOperation , String action ) {
123
123
withClient (createMcpTransport (), mcpSyncClient -> {
124
- // This scheduler is not replaced by virtual time scheduler
125
- Scheduler customScheduler = Schedulers .newBoundedElastic (1 , 1 , "actualBoundedElastic" );
126
-
127
- StepVerifier .withVirtualTime (() -> Mono .fromSupplier (() -> blockingOperation .apply (mcpSyncClient ))
128
- // Offload the blocking call to the real scheduler
129
- .subscribeOn (customScheduler ))
130
- .expectSubscription ()
131
- // This works without actually waiting but executes all the
132
- // tasks pending execution on the VirtualTimeScheduler.
133
- // It is possible to execute the blocking code from the operation
134
- // because it is blocked on a dedicated Scheduler and the main
135
- // flow is not blocked and uses the VirtualTimeScheduler.
136
- .thenAwait (getInitializationTimeout ())
137
- .consumeErrorWith (e -> assertThat (e ).isInstanceOf (McpError .class )
138
- .hasMessage ("Client must be initialized before " + action ))
139
- .verify ();
140
-
141
- customScheduler .dispose ();
124
+ StepVerifier .create (Mono .fromSupplier (() -> blockingOperation .apply (mcpSyncClient )))
125
+ .expectNextCount (1 )
126
+ .verifyComplete ();
142
127
});
143
128
}
144
129
@@ -154,7 +139,7 @@ void testConstructorWithInvalidArguments() {
154
139
155
140
@ Test
156
141
void testListToolsWithoutInitialization () {
157
- verifyCallTimesOut (client -> client .listTools (null ), "listing tools" );
142
+ verifyCallSucceedsWithImplicitInitialization (client -> client .listTools (null ), "listing tools" );
158
143
}
159
144
160
145
@ Test
@@ -175,8 +160,8 @@ void testListTools() {
175
160
176
161
@ Test
177
162
void testCallToolsWithoutInitialization () {
178
- verifyCallTimesOut ( client -> client . callTool ( new CallToolRequest ( "add" , Map . of ( "a" , 3 , "b" , 4 ))),
179
- "calling tools" );
163
+ verifyCallSucceedsWithImplicitInitialization (
164
+ client -> client . callTool ( new CallToolRequest ( "add" , Map . of ( "a" , 3 , "b" , 4 ))), "calling tools" );
180
165
}
181
166
182
167
@ Test
@@ -200,7 +185,7 @@ void testCallTools() {
200
185
201
186
@ Test
202
187
void testPingWithoutInitialization () {
203
- verifyCallTimesOut (client -> client .ping (), "pinging the server" );
188
+ verifyCallSucceedsWithImplicitInitialization (client -> client .ping (), "pinging the server" );
204
189
}
205
190
206
191
@ Test
@@ -214,7 +199,7 @@ void testPing() {
214
199
@ Test
215
200
void testCallToolWithoutInitialization () {
216
201
CallToolRequest callToolRequest = new CallToolRequest ("echo" , Map .of ("message" , TEST_MESSAGE ));
217
- verifyCallTimesOut (client -> client .callTool (callToolRequest ), "calling tools" );
202
+ verifyCallSucceedsWithImplicitInitialization (client -> client .callTool (callToolRequest ), "calling tools" );
218
203
}
219
204
220
205
@ Test
@@ -243,7 +228,7 @@ void testCallToolWithInvalidTool() {
243
228
244
229
@ Test
245
230
void testRootsListChangedWithoutInitialization () {
246
- verifyNotificationTimesOut (client -> client .rootsListChangedNotification (),
231
+ verifyNotificationSucceedsWithImplicitInitialization (client -> client .rootsListChangedNotification (),
247
232
"sending roots list changed notification" );
248
233
}
249
234
@@ -257,7 +242,7 @@ void testRootsListChanged() {
257
242
258
243
@ Test
259
244
void testListResourcesWithoutInitialization () {
260
- verifyCallTimesOut (client -> client .listResources (null ), "listing resources" );
245
+ verifyCallSucceedsWithImplicitInitialization (client -> client .listResources (null ), "listing resources" );
261
246
}
262
247
263
248
@ Test
@@ -333,8 +318,14 @@ void testRemoveNonExistentRoot() {
333
318
334
319
@ Test
335
320
void testReadResourceWithoutInitialization () {
336
- Resource resource = new Resource ("test://uri" , "Test Resource" , null , null , null );
337
- verifyCallTimesOut (client -> client .readResource (resource ), "reading resources" );
321
+ AtomicReference <List <Resource >> resources = new AtomicReference <>();
322
+ withClient (createMcpTransport (), mcpSyncClient -> {
323
+ mcpSyncClient .initialize ();
324
+ resources .set (mcpSyncClient .listResources ().resources ());
325
+ });
326
+
327
+ verifyCallSucceedsWithImplicitInitialization (client -> client .readResource (resources .get ().get (0 )),
328
+ "reading resources" );
338
329
}
339
330
340
331
@ Test
@@ -355,7 +346,8 @@ void testReadResource() {
355
346
356
347
@ Test
357
348
void testListResourceTemplatesWithoutInitialization () {
358
- verifyCallTimesOut (client -> client .listResourceTemplates (null ), "listing resource templates" );
349
+ verifyCallSucceedsWithImplicitInitialization (client -> client .listResourceTemplates (null ),
350
+ "listing resource templates" );
359
351
}
360
352
361
353
@ Test
@@ -413,8 +405,8 @@ void testNotificationHandlers() {
413
405
414
406
@ Test
415
407
void testLoggingLevelsWithoutInitialization () {
416
- verifyNotificationTimesOut ( client -> client . setLoggingLevel ( McpSchema . LoggingLevel . DEBUG ),
417
- "setting logging level" );
408
+ verifyNotificationSucceedsWithImplicitInitialization (
409
+ client -> client . setLoggingLevel ( McpSchema . LoggingLevel . DEBUG ), "setting logging level" );
418
410
}
419
411
420
412
@ Test
0 commit comments