Skip to content

Commit 5ab9365

Browse files
tzolovilayaperumalg
authored andcommitted
refactor: Migrate MCP test to use autoconfiguration and add ChatClient integration
Replace manual MCP annotation providers with autoconfiguration beans. Add ChatClient integration test with Anthropic model in sampling handler. Signed-off-by: Christian Tzolov <[email protected]>
1 parent 110605f commit 5ab9365

File tree

2 files changed

+60
-53
lines changed

2 files changed

+60
-53
lines changed

auto-configurations/mcp/spring-ai-autoconfigure-mcp-server-webflux/pom.xml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,27 @@
9595
<scope>test</scope>
9696
</dependency>
9797

98+
<dependency>
99+
<groupId>org.springframework.ai</groupId>
100+
<artifactId>spring-ai-autoconfigure-model-anthropic</artifactId>
101+
<version>${project.parent.version}</version>
102+
<scope>test</scope>
103+
</dependency>
104+
105+
<dependency>
106+
<groupId>org.springframework.ai</groupId>
107+
<artifactId>spring-ai-anthropic</artifactId>
108+
<version>${project.parent.version}</version>
109+
<scope>test</scope>
110+
</dependency>
111+
112+
<dependency>
113+
<groupId>org.springframework.ai</groupId>
114+
<artifactId>spring-ai-autoconfigure-model-chat-client</artifactId>
115+
<version>${project.parent.version}</version>
116+
<scope>test</scope>
117+
</dependency>
118+
98119
</dependencies>
99120

100121
</project>

auto-configurations/mcp/spring-ai-autoconfigure-mcp-server-webflux/src/test/java/org/springframework/ai/mcp/server/autoconfigure/StreamableMcpAnnotationsManualIT.java

Lines changed: 39 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@
2727

2828
import com.fasterxml.jackson.databind.ObjectMapper;
2929
import io.modelcontextprotocol.client.McpSyncClient;
30-
import io.modelcontextprotocol.server.McpServerFeatures;
3130
import io.modelcontextprotocol.server.McpSyncServer;
3231
import io.modelcontextprotocol.server.McpSyncServerExchange;
3332
import io.modelcontextprotocol.server.transport.WebFluxStreamableServerTransportProvider;
@@ -54,6 +53,7 @@
5453
import net.javacrumbs.jsonunit.assertj.JsonAssertions;
5554
import net.javacrumbs.jsonunit.core.Option;
5655
import org.junit.jupiter.api.Test;
56+
import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable;
5757
import org.slf4j.Logger;
5858
import org.slf4j.LoggerFactory;
5959
import org.springaicommunity.mcp.annotation.McpArg;
@@ -68,21 +68,23 @@
6868
import org.springaicommunity.mcp.annotation.McpSampling;
6969
import org.springaicommunity.mcp.annotation.McpTool;
7070
import 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;
7571
import reactor.netty.DisposableServer;
7672
import reactor.netty.http.server.HttpServer;
7773

78-
import org.springframework.ai.mcp.annotation.spring.SyncMcpAnnotationProviders;
74+
import org.springframework.ai.chat.client.ChatClient;
7975
import org.springframework.ai.mcp.client.common.autoconfigure.McpClientAutoConfiguration;
8076
import 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;
8179
import org.springframework.ai.mcp.client.webflux.autoconfigure.StreamableHttpWebFluxTransportAutoConfiguration;
8280
import org.springframework.ai.mcp.server.common.autoconfigure.McpServerAutoConfiguration;
8381
import 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;
8484
import org.springframework.ai.mcp.server.common.autoconfigure.properties.McpServerProperties;
8585
import 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;
8688
import org.springframework.beans.factory.ObjectProvider;
8789
import org.springframework.boot.autoconfigure.AutoConfigurations;
8890
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
@@ -98,16 +100,22 @@
98100
import static org.assertj.core.api.Assertions.assertThat;
99101
import static org.assertj.core.api.InstanceOfAssertFactories.map;
100102

103+
@EnabledIfEnvironmentVariable(named = "ANTHROPIC_API_KEY", matches = ".+")
101104
public class StreamableMcpAnnotationsManualIT {
102105

103106
private final ApplicationContextRunner serverContextRunner = new ApplicationContextRunner()
104107
.withPropertyValues("spring.ai.mcp.server.protocol=STREAMABLE")
105-
.withConfiguration(AutoConfigurations.of(McpServerAutoConfiguration.class,
108+
.withConfiguration(AutoConfigurations.of(McpServerAnnotationScannerAutoConfiguration.class,
109+
McpServerSpecificationFactoryAutoConfiguration.class, McpServerAutoConfiguration.class,
106110
ToolCallbackConverterAutoConfiguration.class, McpServerStreamableHttpWebFluxAutoConfiguration.class));
107111

108112
private final ApplicationContextRunner clientApplicationContext = new ApplicationContextRunner()
109113
.withConfiguration(AutoConfigurations.of(McpToolCallbackAutoConfiguration.class,
110-
McpClientAutoConfiguration.class, StreamableHttpWebFluxTransportAutoConfiguration.class));
114+
McpClientAutoConfiguration.class, StreamableHttpWebFluxTransportAutoConfiguration.class,
115+
// MCP Annotations
116+
McpClientAnnotationScannerAutoConfiguration.class, McpClientSpecificationFactoryAutoConfiguration.class,
117+
// Anthropic ChatClient Builder
118+
AnthropicChatAutoConfiguration.class, ChatClientAutoConfiguration.class));
111119

112120
@Test
113121
void clientServerCapabilities() {
@@ -141,6 +149,7 @@ void clientServerCapabilities() {
141149

142150
this.clientApplicationContext.withUserConfiguration(TestMcpClientConfiguration.class)
143151
.withPropertyValues(// @formatter:off
152+
"spring.ai.anthropic.api-key=" + System.getenv("ANTHROPIC_API_KEY"),
144153
"spring.ai.mcp.client.streamable-http.connections.server1.url=http://localhost:" + serverPort,
145154
// "spring.ai.mcp.client.request-timeout=20m",
146155
"spring.ai.mcp.client.initialized=false") // @formatter:on
@@ -306,28 +315,6 @@ public McpServerHandlers serverSideSpecProviders() {
306315
return new McpServerHandlers();
307316
}
308317

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-
331318
public static class McpServerHandlers {
332319

333320
@McpTool(description = "Test tool", name = "tool1")
@@ -449,28 +436,9 @@ public TestContext testContext() {
449436
}
450437

451438
@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));
439+
public McpClientHandlers mcpClientHandlers(TestContext testContext,
440+
ObjectProvider<ChatClient.Builder> chatClientBuilderProvider) {
441+
return new McpClientHandlers(testContext, chatClientBuilderProvider);
474442
}
475443

476444
public static class TestContext {
@@ -489,8 +457,21 @@ public static class McpClientHandlers {
489457

490458
private TestContext testContext;
491459

492-
public McpClientHandlers(TestContext testContext) {
460+
private final ObjectProvider<ChatClient.Builder> chatClientBuilderProvider;
461+
462+
private AtomicReference<ChatClient> chatClientRef = new AtomicReference<>();
463+
464+
private ChatClient chatClient() {
465+
if (this.chatClientRef.get() == null) {
466+
this.chatClientRef.compareAndSet(null, this.chatClientBuilderProvider.getIfAvailable().build());
467+
}
468+
return this.chatClientRef.get();
469+
}
470+
471+
public McpClientHandlers(TestContext testContext,
472+
ObjectProvider<ChatClient.Builder> chatClientBuilderProvider) {
493473
this.testContext = testContext;
474+
this.chatClientBuilderProvider = chatClientBuilderProvider;
494475
}
495476

496477
@McpProgress(clients = "server1")
@@ -515,6 +496,11 @@ public CreateMessageResult samplingHandler(CreateMessageRequest llmRequest) {
515496
String userPrompt = ((McpSchema.TextContent) llmRequest.messages().get(0).content()).text();
516497
String modelHint = llmRequest.modelPreferences().hints().get(0).name();
517498

499+
// String joke =
500+
// this.chatClientBuilderProvider.getIfAvailable().build().prompt("Tell me
501+
// a joke").call().content();
502+
String joke = this.chatClient().prompt("Tell me a joke").call().content();
503+
logger.info("Received joke from chat client: {}", joke);
518504
return CreateMessageResult.builder()
519505
.content(new McpSchema.TextContent("Response " + userPrompt + " with model hint " + modelHint))
520506
.build();

0 commit comments

Comments
 (0)