|
1 | 1 | /* |
2 | | - * Copyright 2024-2024 the original author or authors. |
| 2 | + * Copyright 2024-2025 the original author or authors. |
3 | 3 | */ |
4 | 4 |
|
5 | 5 | package org.springframework.ai.mcp.client.autoconfigure; |
6 | 6 |
|
7 | 7 | import static org.assertj.core.api.Assertions.assertThat; |
| 8 | +import static org.mockito.ArgumentMatchers.any; |
| 9 | +import static org.mockito.Mockito.atLeastOnce; |
| 10 | +import static org.mockito.Mockito.mock; |
| 11 | +import static org.mockito.Mockito.verify; |
| 12 | +import static org.mockito.Mockito.verifyNoInteractions; |
| 13 | +import static org.mockito.Mockito.when; |
8 | 14 |
|
9 | 15 | import java.util.List; |
10 | 16 |
|
|
17 | 23 | import org.springframework.ai.mcp.client.common.autoconfigure.McpClientAutoConfiguration; |
18 | 24 | import org.springframework.ai.mcp.client.httpclient.autoconfigure.StreamableHttpHttpClientTransportAutoConfiguration; |
19 | 25 | import org.springframework.boot.autoconfigure.AutoConfigurations; |
| 26 | +import org.springframework.boot.context.annotation.UserConfigurations; |
20 | 27 | import org.springframework.boot.test.context.runner.ApplicationContextRunner; |
| 28 | +import org.springframework.context.annotation.Bean; |
| 29 | +import org.springframework.context.annotation.Configuration; |
| 30 | + |
21 | 31 | import org.testcontainers.containers.GenericContainer; |
22 | 32 | import org.testcontainers.containers.wait.strategy.Wait; |
23 | 33 |
|
24 | 34 | import io.modelcontextprotocol.client.McpSyncClient; |
| 35 | +import io.modelcontextprotocol.client.transport.AsyncHttpRequestCustomizer; |
| 36 | +import io.modelcontextprotocol.client.transport.SyncHttpRequestCustomizer; |
25 | 37 | import io.modelcontextprotocol.spec.McpSchema.ListToolsResult; |
| 38 | +import reactor.core.publisher.Mono; |
26 | 39 |
|
27 | 40 | @Timeout(15) |
28 | 41 | public class StreamableHttpHttpClientTransportAutoConfigurationIT { |
@@ -80,8 +93,69 @@ void streamableHttpTest() { |
80 | 93 | assertThat(toolsResult.tools()).hasSize(8); |
81 | 94 |
|
82 | 95 | logger.info("tools = {}", toolsResult); |
83 | | - |
84 | 96 | }); |
85 | 97 | } |
86 | 98 |
|
| 99 | + @Test |
| 100 | + void usesSyncRequestCustomizer() { |
| 101 | + this.contextRunner |
| 102 | + .withConfiguration(UserConfigurations.of(SyncRequestCustomizerConfiguration.class, |
| 103 | + AsyncRequestCustomizerConfiguration.class)) |
| 104 | + .run(context -> { |
| 105 | + List<McpSyncClient> mcpClients = (List<McpSyncClient>) context.getBean("mcpSyncClients"); |
| 106 | + |
| 107 | + assertThat(mcpClients).isNotNull(); |
| 108 | + assertThat(mcpClients).hasSize(1); |
| 109 | + |
| 110 | + McpSyncClient mcpClient = mcpClients.get(0); |
| 111 | + |
| 112 | + mcpClient.ping(); |
| 113 | + |
| 114 | + verify(context.getBean(SyncHttpRequestCustomizer.class), atLeastOnce()).customize(any(), any(), any(), |
| 115 | + any()); |
| 116 | + verifyNoInteractions(context.getBean(AsyncHttpRequestCustomizer.class)); |
| 117 | + }); |
| 118 | + } |
| 119 | + |
| 120 | + @Test |
| 121 | + void usesAsyncRequestCustomizer() { |
| 122 | + this.contextRunner.withConfiguration(UserConfigurations.of(AsyncRequestCustomizerConfiguration.class)) |
| 123 | + .run(context -> { |
| 124 | + List<McpSyncClient> mcpClients = (List<McpSyncClient>) context.getBean("mcpSyncClients"); |
| 125 | + |
| 126 | + assertThat(mcpClients).isNotNull(); |
| 127 | + assertThat(mcpClients).hasSize(1); |
| 128 | + |
| 129 | + McpSyncClient mcpClient = mcpClients.get(0); |
| 130 | + |
| 131 | + mcpClient.ping(); |
| 132 | + |
| 133 | + verify(context.getBean(AsyncHttpRequestCustomizer.class), atLeastOnce()).customize(any(), any(), any(), |
| 134 | + any()); |
| 135 | + }); |
| 136 | + } |
| 137 | + |
| 138 | + @Configuration |
| 139 | + static class SyncRequestCustomizerConfiguration { |
| 140 | + |
| 141 | + @Bean |
| 142 | + SyncHttpRequestCustomizer syncHttpRequestCustomizer() { |
| 143 | + return mock(SyncHttpRequestCustomizer.class); |
| 144 | + } |
| 145 | + |
| 146 | + } |
| 147 | + |
| 148 | + @Configuration |
| 149 | + static class AsyncRequestCustomizerConfiguration { |
| 150 | + |
| 151 | + @Bean |
| 152 | + AsyncHttpRequestCustomizer asyncHttpRequestCustomizer() { |
| 153 | + AsyncHttpRequestCustomizer requestCustomizerMock = mock(AsyncHttpRequestCustomizer.class); |
| 154 | + when(requestCustomizerMock.customize(any(), any(), any(), any())) |
| 155 | + .thenAnswer(invocation -> Mono.just(invocation.getArguments()[0])); |
| 156 | + return requestCustomizerMock; |
| 157 | + } |
| 158 | + |
| 159 | + } |
| 160 | + |
87 | 161 | } |
0 commit comments