2727
2828import com .fasterxml .jackson .databind .ObjectMapper ;
2929import io .modelcontextprotocol .client .McpSyncClient ;
30- import io .modelcontextprotocol .server .McpServerFeatures ;
3130import io .modelcontextprotocol .server .McpSyncServer ;
3231import io .modelcontextprotocol .server .McpSyncServerExchange ;
3332import io .modelcontextprotocol .server .transport .WebFluxStreamableServerTransportProvider ;
5453import net .javacrumbs .jsonunit .assertj .JsonAssertions ;
5554import net .javacrumbs .jsonunit .core .Option ;
5655import org .junit .jupiter .api .Test ;
56+ import org .junit .jupiter .api .condition .EnabledIfEnvironmentVariable ;
5757import org .slf4j .Logger ;
5858import org .slf4j .LoggerFactory ;
5959import org .springaicommunity .mcp .annotation .McpArg ;
6868import org .springaicommunity .mcp .annotation .McpSampling ;
6969import org .springaicommunity .mcp .annotation .McpTool ;
7070import org .springaicommunity .mcp .annotation .McpToolParam ;
71- import org .springaicommunity .mcp .method .elicitation .SyncElicitationSpecification ;
72- import org .springaicommunity .mcp .method .logging .SyncLoggingSpecification ;
73- import org .springaicommunity .mcp .method .progress .SyncProgressSpecification ;
74- import org .springaicommunity .mcp .method .sampling .SyncSamplingSpecification ;
7571import reactor .netty .DisposableServer ;
7672import reactor .netty .http .server .HttpServer ;
7773
78- import org .springframework .ai .mcp . annotation . spring . SyncMcpAnnotationProviders ;
74+ import org .springframework .ai .chat . client . ChatClient ;
7975import org .springframework .ai .mcp .client .common .autoconfigure .McpClientAutoConfiguration ;
8076import org .springframework .ai .mcp .client .common .autoconfigure .McpToolCallbackAutoConfiguration ;
77+ import org .springframework .ai .mcp .client .common .autoconfigure .annotations .McpClientAnnotationScannerAutoConfiguration ;
78+ import org .springframework .ai .mcp .client .common .autoconfigure .annotations .McpClientSpecificationFactoryAutoConfiguration ;
8179import org .springframework .ai .mcp .client .webflux .autoconfigure .StreamableHttpWebFluxTransportAutoConfiguration ;
8280import org .springframework .ai .mcp .server .common .autoconfigure .McpServerAutoConfiguration ;
8381import org .springframework .ai .mcp .server .common .autoconfigure .ToolCallbackConverterAutoConfiguration ;
82+ import org .springframework .ai .mcp .server .common .autoconfigure .annotations .McpServerAnnotationScannerAutoConfiguration ;
83+ import org .springframework .ai .mcp .server .common .autoconfigure .annotations .McpServerSpecificationFactoryAutoConfiguration ;
8484import org .springframework .ai .mcp .server .common .autoconfigure .properties .McpServerProperties ;
8585import org .springframework .ai .mcp .server .common .autoconfigure .properties .McpServerStreamableHttpProperties ;
86+ import org .springframework .ai .model .anthropic .autoconfigure .AnthropicChatAutoConfiguration ;
87+ import org .springframework .ai .model .chat .client .autoconfigure .ChatClientAutoConfiguration ;
8688import org .springframework .beans .factory .ObjectProvider ;
8789import org .springframework .boot .autoconfigure .AutoConfigurations ;
8890import org .springframework .boot .test .context .runner .ApplicationContextRunner ;
8991import org .springframework .context .ApplicationContext ;
9092import org .springframework .context .annotation .Bean ;
93+ import org .springframework .context .annotation .Lazy ;
9194import org .springframework .core .ResolvableType ;
9295import org .springframework .http .server .reactive .HttpHandler ;
9396import org .springframework .http .server .reactive .ReactorHttpHandlerAdapter ;
97+ import org .springframework .stereotype .Service ;
9498import org .springframework .test .util .TestSocketUtils ;
9599import org .springframework .web .reactive .function .server .RouterFunction ;
96100import org .springframework .web .reactive .function .server .RouterFunctions ;
97101
98102import static org .assertj .core .api .Assertions .assertThat ;
99103import static org .assertj .core .api .InstanceOfAssertFactories .map ;
100104
105+ @ EnabledIfEnvironmentVariable (named = "ANTHROPIC_API_KEY" , matches = ".+" )
101106public class StreamableMcpAnnotationsManualIT {
102107
103108 private final ApplicationContextRunner serverContextRunner = new ApplicationContextRunner ()
104109 .withPropertyValues ("spring.ai.mcp.server.protocol=STREAMABLE" )
105- .withConfiguration (AutoConfigurations .of (McpServerAutoConfiguration .class ,
110+ .withConfiguration (AutoConfigurations .of (McpServerAnnotationScannerAutoConfiguration .class ,
111+ McpServerSpecificationFactoryAutoConfiguration .class , McpServerAutoConfiguration .class ,
106112 ToolCallbackConverterAutoConfiguration .class , McpServerStreamableHttpWebFluxAutoConfiguration .class ));
107113
108114 private final ApplicationContextRunner clientApplicationContext = new ApplicationContextRunner ()
109115 .withConfiguration (AutoConfigurations .of (McpToolCallbackAutoConfiguration .class ,
110- McpClientAutoConfiguration .class , StreamableHttpWebFluxTransportAutoConfiguration .class ));
116+ McpClientAutoConfiguration .class , StreamableHttpWebFluxTransportAutoConfiguration .class ,
117+ // MCP Annotations
118+ McpClientAnnotationScannerAutoConfiguration .class , McpClientSpecificationFactoryAutoConfiguration .class ,
119+ // Anthropic ChatClient Builder
120+ AnthropicChatAutoConfiguration .class , ChatClientAutoConfiguration .class ));
111121
112122 @ Test
113123 void clientServerCapabilities () {
@@ -141,6 +151,7 @@ void clientServerCapabilities() {
141151
142152 this .clientApplicationContext .withUserConfiguration (TestMcpClientConfiguration .class )
143153 .withPropertyValues (// @formatter:off
154+ "spring.ai.anthropic.api-key=" + System .getenv ("ANTHROPIC_API_KEY" ),
144155 "spring.ai.mcp.client.streamable-http.connections.server1.url=http://localhost:" + serverPort ,
145156 // "spring.ai.mcp.client.request-timeout=20m",
146157 "spring.ai.mcp.client.initialized=false" ) // @formatter:on
@@ -306,28 +317,6 @@ public McpServerHandlers serverSideSpecProviders() {
306317 return new McpServerHandlers ();
307318 }
308319
309- @ Bean
310- public List <McpServerFeatures .SyncToolSpecification > myTools (McpServerHandlers serverSideSpecProviders ) {
311- return SyncMcpAnnotationProviders .toolSpecifications (List .of (serverSideSpecProviders ));
312- }
313-
314- @ Bean
315- public List <McpServerFeatures .SyncResourceSpecification > myResources (
316- McpServerHandlers serverSideSpecProviders ) {
317- return SyncMcpAnnotationProviders .resourceSpecifications (List .of (serverSideSpecProviders ));
318- }
319-
320- @ Bean
321- public List <McpServerFeatures .SyncPromptSpecification > myPrompts (McpServerHandlers serverSideSpecProviders ) {
322- return SyncMcpAnnotationProviders .promptSpecifications (List .of (serverSideSpecProviders ));
323- }
324-
325- @ Bean
326- public List <McpServerFeatures .SyncCompletionSpecification > myCompletions (
327- McpServerHandlers serverSideSpecProviders ) {
328- return SyncMcpAnnotationProviders .completeSpecifications (List .of (serverSideSpecProviders ));
329- }
330-
331320 public static class McpServerHandlers {
332321
333322 @ McpTool (description = "Test tool" , name = "tool1" )
@@ -449,28 +438,9 @@ public TestContext testContext() {
449438 }
450439
451440 @ Bean
452- public McpClientHandlers mcpClientHandlers (TestContext testContext ) {
453- return new McpClientHandlers (testContext );
454- }
455-
456- @ Bean
457- List <SyncLoggingSpecification > loggingSpecs (McpClientHandlers clientMcpHandlers ) {
458- return SyncMcpAnnotationProviders .loggingSpecifications (List .of (clientMcpHandlers ));
459- }
460-
461- @ Bean
462- List <SyncSamplingSpecification > samplingSpecs (McpClientHandlers clientMcpHandlers ) {
463- return SyncMcpAnnotationProviders .samplingSpecifications (List .of (clientMcpHandlers ));
464- }
465-
466- @ Bean
467- List <SyncElicitationSpecification > elicitationSpecs (McpClientHandlers clientMcpHandlers ) {
468- return SyncMcpAnnotationProviders .elicitationSpecifications (List .of (clientMcpHandlers ));
469- }
470-
471- @ Bean
472- List <SyncProgressSpecification > progressSpecs (McpClientHandlers clientMcpHandlers ) {
473- return SyncMcpAnnotationProviders .progressSpecifications (List .of (clientMcpHandlers ));
441+ public McpClientHandlers mcpClientHandlers (TestContext testContext ,
442+ ObjectProvider <ChatClient .Builder > chatClientBuilderProvider ) {
443+ return new McpClientHandlers (testContext , chatClientBuilderProvider );
474444 }
475445
476446 public static class TestContext {
@@ -489,8 +459,21 @@ public static class McpClientHandlers {
489459
490460 private TestContext testContext ;
491461
492- public McpClientHandlers (TestContext testContext ) {
462+ private final ObjectProvider <ChatClient .Builder > chatClientBuilderProvider ;
463+
464+ private AtomicReference <ChatClient > chatClientRef = new AtomicReference <>();
465+
466+ private ChatClient chatClient () {
467+ if (this .chatClientRef .get () == null ) {
468+ this .chatClientRef .compareAndSet (null , this .chatClientBuilderProvider .getIfAvailable ().build ());
469+ }
470+ return this .chatClientRef .get ();
471+ }
472+
473+ public McpClientHandlers (TestContext testContext ,
474+ ObjectProvider <ChatClient .Builder > chatClientBuilderProvider ) {
493475 this .testContext = testContext ;
476+ this .chatClientBuilderProvider = chatClientBuilderProvider ;
494477 }
495478
496479 @ McpProgress (clients = "server1" )
@@ -515,6 +498,11 @@ public CreateMessageResult samplingHandler(CreateMessageRequest llmRequest) {
515498 String userPrompt = ((McpSchema .TextContent ) llmRequest .messages ().get (0 ).content ()).text ();
516499 String modelHint = llmRequest .modelPreferences ().hints ().get (0 ).name ();
517500
501+ // String joke =
502+ // this.chatClientBuilderProvider.getIfAvailable().build().prompt("Tell me
503+ // a joke").call().content();
504+ String joke = this .chatClient ().prompt ("Tell me a joke" ).call ().content ();
505+ logger .info ("Received joke from chat client: {}" , joke );
518506 return CreateMessageResult .builder ()
519507 .content (new McpSchema .TextContent ("Response " + userPrompt + " with model hint " + modelHint ))
520508 .build ();
0 commit comments