Skip to content

Commit fc1323c

Browse files
committed
feat(mcp): add MCP client support and refactor server configuration
- Add MCP client autoconfiguration with support for STDIO, WebMVC and WebFlux transports - Rename spring-ai-starter-mcp-server to spring-ai-starter-mcp to reflect dual client/server support - Add close() method to McpToolCallback for proper resource cleanup - Add initialize flag to control MCP client initialization - Add comprehensive integration tests and documentation for MCP client configuration - Fix MCP server auto-configuration tests Signed-off-by: Christian Tzolov <[email protected]>
1 parent bb15f36 commit fc1323c

File tree

19 files changed

+1335
-11
lines changed

19 files changed

+1335
-11
lines changed

mcp/common/src/main/java/org/springframework/ai/mcp/McpToolCallback.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,4 +112,8 @@ public String call(String functionInput) {
112112
return ModelOptionsUtils.toJsonString(response.content());
113113
}
114114

115+
public void close() {
116+
this.mcpClient.close();
117+
}
118+
115119
}

pom.xml

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,8 @@
126126
<module>spring-ai-spring-boot-starters/spring-ai-starter-watsonx-ai</module>
127127
<module>spring-ai-spring-boot-starters/spring-ai-starter-zhipuai</module>
128128
<module>spring-ai-spring-boot-starters/spring-ai-starter-moonshot</module>
129-
<module>spring-ai-spring-boot-starters/spring-ai-starter-mcp-server</module>
129+
130+
<module>spring-ai-spring-boot-starters/spring-ai-starter-mcp</module>
130131

131132
<module>spring-ai-integration-tests</module>
132133

@@ -851,11 +852,22 @@
851852
</dependencyManagement>
852853

853854
<repositories>
855+
<repository>
856+
<name>Central Portal Snapshots</name>
857+
<id>central-portal-snapshots</id>
858+
<url>https://central.sonatype.com/repository/maven-snapshots/</url>
859+
<releases>
860+
<enabled>false</enabled>
861+
</releases>
862+
<snapshots>
863+
<enabled>true</enabled>
864+
</snapshots>
865+
</repository>
854866
<repository>
855867
<id>maven-central</id>
856868
<url>https://repo.maven.apache.org/maven2/</url>
857869
<snapshots>
858-
<enabled>false</enabled>
870+
<enabled>true</enabled>
859871
</snapshots>
860872
<releases>
861873
<enabled>true</enabled>

spring-ai-bom/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -589,7 +589,7 @@
589589

590590
<dependency>
591591
<groupId>org.springframework.ai</groupId>
592-
<artifactId>spring-ai-mcp-server-spring-boot-starter</artifactId>
592+
<artifactId>spring-ai-mcp-spring-boot-starter</artifactId>
593593
<version>${project.version}</version>
594594
</dependency>
595595

spring-ai-docs/src/main/asciidoc/mcp.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ To use the MCP server functionality, add the following dependency to your projec
99
```xml
1010
<dependency>
1111
<groupId>org.springframework.ai</groupId>
12-
<artifactId>spring-ai-mcp-server-spring-boot-starter</artifactId>
12+
<artifactId>spring-ai-mcp-spring-boot-starter</artifactId>
1313
<version>${spring-ai.version}</version>
1414
</dependency>
1515
```
@@ -20,7 +20,7 @@ The MCP server can be configured using the following properties under the `sprin
2020

2121
| Property | Default | Description |
2222
|----------|---------|-------------|
23-
| `enabled` | `true` | Enable/disable the MCP server |
23+
| `enabled` | `false` | Enable/disable the MCP server |
2424
| `name` | `"mcp-server"` | Name of the MCP server |
2525
| `version` | `"1.0.0"` | Version of the MCP server |
2626
| `type` | `SYNC` | Server type (`SYNC` or `ASYNC`) |
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/*
2+
* Copyright 2025-2025 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.ai.autoconfigure.mcp.client;
18+
19+
import io.modelcontextprotocol.client.transport.HttpClientSseClientTransport;
20+
21+
import org.springframework.boot.autoconfigure.AutoConfiguration;
22+
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
23+
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
24+
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
25+
import org.springframework.context.annotation.Bean;
26+
27+
@AutoConfiguration
28+
@ConditionalOnClass({ HttpClientSseClientTransport.class })
29+
@ConditionalOnProperty(prefix = McpClientProperties.CONFIG_PREFIX, name = "transport", havingValue = "WEBMVC")
30+
public class HttpClientSseClientAutoConfiguration {
31+
32+
@Bean
33+
@ConditionalOnMissingBean
34+
public HttpClientSseClientTransport webHttpTransport(McpClientProperties properties) {
35+
return new HttpClientSseClientTransport(properties.getBaseUrl());
36+
}
37+
38+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
/*
2+
* Copyright 2025-2025 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.ai.autoconfigure.mcp.client;
18+
19+
import java.time.Duration;
20+
21+
import org.springframework.boot.context.properties.ConfigurationProperties;
22+
import org.springframework.util.Assert;
23+
24+
@ConfigurationProperties(McpBasicClientProperties.CONFIG_PREFIX)
25+
public class McpBasicClientProperties {
26+
27+
public static final String CONFIG_PREFIX = "spring.ai.mcp.client";
28+
29+
/**
30+
* Enable/disable the MCP client.
31+
* <p>
32+
* When set to false, the MCP client and all its components will not be initialized.
33+
*/
34+
private boolean enabled = true;
35+
36+
/**
37+
* The name of the MCP client instance.
38+
* <p>
39+
* This name is used to identify the server in logs and monitoring.
40+
*/
41+
private String name = "mcp-client";
42+
43+
/**
44+
* The version of the MCP client instance.
45+
* <p>
46+
* This version is reported to clients and used for compatibility checks.
47+
*/
48+
private String version = "1.0.0";
49+
50+
private Duration requestTimeout = Duration.ofSeconds(20); // Default timeout
51+
52+
private boolean rootChangeNotification = true;
53+
54+
/**
55+
* The transport type to use for MCP server communication.
56+
* <p>
57+
* Supported types are:
58+
* <ul>
59+
* <li>STDIO - Standard input/output transport (default)</li>
60+
* <li>WEBMVC - Spring MVC Server-Sent Events transport</li>
61+
* <li>WEBFLUX - Spring WebFlux Server-Sent Events transport</li>
62+
* </ul>
63+
*/
64+
private Transport transport = Transport.STDIO;
65+
66+
private String baseUrl;
67+
68+
private McpStdioConnection stdioConnection = new McpStdioConnection();
69+
70+
/**
71+
* The type of server to use for MCP server communication.
72+
* <p>
73+
* Supported types are:
74+
* <ul>
75+
* <li>SYNC - Standard synchronous server (default)</li>
76+
* <li>ASYNC - Asynchronous server</li>
77+
* </ul>
78+
*/
79+
private ClientType type = ClientType.SYNC;
80+
81+
/**
82+
* Transport types supported by the MCP server.
83+
*/
84+
public enum Transport {
85+
86+
/**
87+
* Standard input/output transport, suitable for command-line tools and local
88+
* development.
89+
*/
90+
STDIO,
91+
92+
/**
93+
* Spring MVC Server-Sent Events transport, requires spring-boot-starter-web and
94+
* mcp-spring-webmvc.
95+
*/
96+
WEBMVC,
97+
98+
/**
99+
* Spring WebFlux Server-Sent Events transport, requires
100+
* spring-boot-starter-webflux and mcp-spring-webflux.
101+
*/
102+
WEBFLUX
103+
104+
}
105+
106+
/**
107+
* Server types supported by the MCP client.
108+
*/
109+
public enum ClientType {
110+
111+
/**
112+
* Synchronous (McpSyncServer) server
113+
*/
114+
SYNC,
115+
/**
116+
* Asynchronous (McpAsyncServer) server
117+
*/
118+
ASYNC
119+
120+
}
121+
122+
public McpStdioConnection getStdioConnection() {
123+
return this.stdioConnection;
124+
}
125+
126+
public boolean isRootChangeNotification() {
127+
return this.rootChangeNotification;
128+
}
129+
130+
public void setRootChangeNotification(boolean rootChangeNotification) {
131+
this.rootChangeNotification = rootChangeNotification;
132+
}
133+
134+
public Duration getRequestTimeout() {
135+
return this.requestTimeout;
136+
}
137+
138+
public void setRequestTimeout(Duration requestTimeout) {
139+
Assert.notNull(requestTimeout, "Request timeout must not be null");
140+
this.requestTimeout = requestTimeout;
141+
}
142+
143+
public boolean isEnabled() {
144+
return this.enabled;
145+
}
146+
147+
public void setEnabled(boolean enabled) {
148+
this.enabled = enabled;
149+
}
150+
151+
public String getName() {
152+
return this.name;
153+
}
154+
155+
public void setName(String name) {
156+
Assert.hasText(name, "Name must not be empty");
157+
this.name = name;
158+
}
159+
160+
public String getVersion() {
161+
return this.version;
162+
}
163+
164+
public void setVersion(String version) {
165+
Assert.hasText(version, "Version must not be empty");
166+
this.version = version;
167+
}
168+
169+
public Transport getTransport() {
170+
return this.transport;
171+
}
172+
173+
public void setTransport(Transport transport) {
174+
Assert.notNull(transport, "Transport must not be null");
175+
this.transport = transport;
176+
}
177+
178+
public String getBaseUrl() {
179+
return this.baseUrl;
180+
}
181+
182+
public void setBaseUrl(String sseUrl) {
183+
this.baseUrl = sseUrl;
184+
}
185+
186+
public ClientType getType() {
187+
return this.type;
188+
}
189+
190+
public void setType(ClientType serverType) {
191+
Assert.notNull(serverType, "Server type must not be null");
192+
this.type = serverType;
193+
}
194+
195+
}

0 commit comments

Comments
 (0)