Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
a654819
feat(mcp): support read config from .yml
digitzh Aug 29, 2025
693c39c
Merge branch 'alibaba:main' into main
digitzh Sep 1, 2025
6106b85
add license
digitzh Sep 1, 2025
826dc0b
style(mcp): spring-javaformat fix
digitzh Sep 1, 2025
97a4e5f
Merge branch 'alibaba:main' into main
digitzh Sep 1, 2025
0992beb
Update auto-configurations/spring-ai-alibaba-autoconfigure-mcp-router…
digitzh Sep 2, 2025
0962b8a
Removing placeholder comment
digitzh Sep 2, 2025
3f3d8c5
Merge branch 'alibaba:main' into main
digitzh Sep 4, 2025
05e8fb6
style: use English comments
digitzh Sep 4, 2025
4c9ca11
feat: use auto configuration for discovery services
digitzh Sep 4, 2025
6d9b95b
style: spring-javaformat fix
digitzh Sep 4, 2025
5b61b40
style: spring-javaformat fix
digitzh Sep 4, 2025
299a442
Merge branch 'main' of github.com:digitzh/spring-ai-alibaba
digitzh Sep 4, 2025
885c380
Merge branch 'alibaba:main' into main
digitzh Sep 8, 2025
7c46365
fix(mcp): merge @ConditionalOnExpression
digitzh Sep 8, 2025
4661573
style: spring-javaformat fix
digitzh Sep 8, 2025
6f7e836
feat(mcp): support read config from database (MySQL, etc.)
digitzh Sep 9, 2025
1dcdd68
fix(mcp): 优化资源关闭逻辑
digitzh Sep 10, 2025
95b6b81
fix(mcp): use @AutoConfiguration
digitzh Sep 10, 2025
e10260a
Merge branch 'alibaba:main' into feature/config_db
digitzh Sep 11, 2025
e2dd037
Merge branch 'main' into feature/config_db
digitzh Sep 16, 2025
e086c9a
feat(mcp): support multi-source read of MCP service
digitzh Sep 18, 2025
00c46a1
Merge branch 'alibaba:main' into feature/config_db
digitzh Sep 18, 2025
a9c7069
fix(mcp): make log message generic
digitzh Sep 18, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/*
* Copyright 2024-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.alibaba.cloud.ai.autoconfigure.mcp.router;

import com.alibaba.cloud.ai.mcp.router.config.McpRouterProperties;
import com.alibaba.cloud.ai.mcp.router.config.DbMcpProperties;
import com.alibaba.cloud.ai.mcp.router.core.discovery.McpServiceDiscovery;
import com.alibaba.cloud.ai.mcp.router.core.discovery.DbMcpServiceDiscovery;
import com.alibaba.cloud.ai.mcp.router.core.discovery.McpServiceDiscoveryFactory;
import jakarta.annotation.PostConstruct;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ai.mcp.server.autoconfigure.McpServerProperties;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;

/**
* Register DbMcpServiceDiscovery to McpServiceDiscoveryFactory.
*
* @author digitzh
*/
@AutoConfiguration
@AutoConfigureAfter(McpServiceDiscoveryAutoConfiguration.class)
@EnableConfigurationProperties({ McpRouterProperties.class, DbMcpProperties.class, McpServerProperties.class })
@ConditionalOnProperty(prefix = McpRouterProperties.CONFIG_PREFIX, name = "enabled", havingValue = "true",
matchIfMissing = true)
public class DbMcpRouterAutoConfiguration {

private static final Logger log = LoggerFactory.getLogger(DbMcpRouterAutoConfiguration.class);

@Bean
@ConditionalOnProperty(prefix = DbMcpProperties.CONFIG_PREFIX, name = "enabled", havingValue = "true")
public DbMcpServiceDiscoveryRegistrar dbMcpServiceDiscoveryRegistrar(McpServiceDiscoveryFactory discoveryFactory,
DbMcpProperties dbMcpProperties) {
log.info("Creating database MCP service discovery registrar with properties: {}", dbMcpProperties);
return new DbMcpServiceDiscoveryRegistrar(discoveryFactory, dbMcpProperties);
}

public static class DbMcpServiceDiscoveryRegistrar {

private final McpServiceDiscoveryFactory discoveryFactory;

private final DbMcpProperties dbMcpProperties;

public DbMcpServiceDiscoveryRegistrar(McpServiceDiscoveryFactory discoveryFactory,
DbMcpProperties dbMcpProperties) {
this.discoveryFactory = discoveryFactory;
this.dbMcpProperties = dbMcpProperties;
log.info("Database MCP service discovery registrar constructor called with properties: {}",
dbMcpProperties);
}

@PostConstruct
public void init() {
log.info("Database MCP service discovery registrar initialized with properties: {}", dbMcpProperties);
log.info("Registering DB MCP service discovery with configuration: {}", dbMcpProperties);
McpServiceDiscovery dbDiscovery = new DbMcpServiceDiscovery(dbMcpProperties);
discoveryFactory.registerDiscovery("database", dbDiscovery);
}

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
* Copyright 2024-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.alibaba.cloud.ai.autoconfigure.mcp.router;

import com.alibaba.cloud.ai.mcp.router.config.McpRouterProperties;
import com.alibaba.cloud.ai.mcp.router.core.discovery.FileConfigMcpServiceDiscovery;
import com.alibaba.cloud.ai.mcp.router.core.discovery.McpServiceDiscovery;
import com.alibaba.cloud.ai.mcp.router.core.discovery.McpServiceDiscoveryFactory;
import jakarta.annotation.PostConstruct;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ai.mcp.server.autoconfigure.McpServerProperties;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;

/**
* Register FileConfigMcpServiceDiscovery to McpServiceDiscoveryFactory.
*
* @author digitzh
*/
@AutoConfiguration
@AutoConfigureAfter(McpServiceDiscoveryAutoConfiguration.class)
@EnableConfigurationProperties({ McpRouterProperties.class, McpServerProperties.class })
@ConditionalOnProperty(prefix = McpRouterProperties.CONFIG_PREFIX, name = "enabled", havingValue = "true",
matchIfMissing = true)
public class FileMcpRouterAutoConfiguration {

private static final Logger log = LoggerFactory.getLogger(FileMcpRouterAutoConfiguration.class);

@Bean
public FileMcpServiceDiscoveryRegistrar fileMcpServiceDiscoveryRegistrar(
McpServiceDiscoveryFactory discoveryFactory, McpRouterProperties properties) {
log.info("Creating file MCP service discovery registrar with properties: {}", properties);
return new FileMcpServiceDiscoveryRegistrar(discoveryFactory, properties);
}

public static class FileMcpServiceDiscoveryRegistrar {

private final McpServiceDiscoveryFactory discoveryFactory;

private final McpRouterProperties properties;

public FileMcpServiceDiscoveryRegistrar(McpServiceDiscoveryFactory discoveryFactory,
McpRouterProperties properties) {
this.discoveryFactory = discoveryFactory;
this.properties = properties;
log.info("File MCP service discovery registrar constructor called with properties: {}", properties);
}

@PostConstruct
public void init() {
log.info("File MCP service discovery registrar initialized with properties: {}", properties);
log.info("Registering file config MCP service discovery with {} services",
properties.getServices() != null ? properties.getServices().size() : 0);
McpServiceDiscovery fileDiscovery = new FileConfigMcpServiceDiscovery(properties);
discoveryFactory.registerDiscovery("file", fileDiscovery);
}

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*
* Copyright 2024-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.alibaba.cloud.ai.autoconfigure.mcp.router;

import com.alibaba.cloud.ai.mcp.router.config.McpRouterProperties;
import com.alibaba.cloud.ai.mcp.router.core.discovery.CompositeMcpServiceDiscovery;
import com.alibaba.cloud.ai.mcp.router.core.discovery.McpServiceDiscovery;
import com.alibaba.cloud.ai.mcp.router.core.discovery.McpServiceDiscoveryFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;

import java.util.List;

/**
* Register McpServiceDiscovery to McpServiceDiscoveryFactory.
*
* @author digitzh
*/
@AutoConfiguration
@EnableConfigurationProperties(McpRouterProperties.class)
@ConditionalOnProperty(prefix = McpRouterProperties.CONFIG_PREFIX, name = "enabled", havingValue = "true",
matchIfMissing = true)
public class McpServiceDiscoveryAutoConfiguration {

private static final Logger log = LoggerFactory.getLogger(McpServiceDiscoveryAutoConfiguration.class);

@Bean
public McpServiceDiscoveryFactory mcpServiceDiscoveryFactory() {
log.info("Creating MCP service discovery factory");
return new McpServiceDiscoveryFactory();
}

@Bean
@Primary
public McpServiceDiscovery compositeMcpServiceDiscovery(McpServiceDiscoveryFactory discoveryFactory,
McpRouterProperties properties) {

List<String> searchOrder = getSearchOrder(properties);
log.info("Creating composite MCP service discovery with search order: {}", searchOrder);

return new CompositeMcpServiceDiscovery(discoveryFactory, searchOrder);
}

private List<String> getSearchOrder(McpRouterProperties properties) {
if (properties.getDiscoveryOrder() != null && !properties.getDiscoveryOrder().isEmpty()) {
return properties.getDiscoveryOrder();
}

return List.of("nacos");
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,14 @@
import com.alibaba.cloud.ai.mcp.router.config.McpRouterProperties;
import com.alibaba.cloud.ai.mcp.router.core.McpRouterWatcher;
import com.alibaba.cloud.ai.mcp.router.core.discovery.McpServiceDiscovery;
import com.alibaba.cloud.ai.mcp.router.core.discovery.McpServiceDiscoveryFactory;
import com.alibaba.cloud.ai.mcp.router.core.vectorstore.McpServerVectorStore;
import com.alibaba.cloud.ai.mcp.router.core.vectorstore.SimpleMcpServerVectorStore;
import com.alibaba.cloud.ai.mcp.router.nacos.NacosMcpServiceDiscovery;
import com.alibaba.cloud.ai.mcp.router.service.McpProxyService;
import com.alibaba.cloud.ai.mcp.router.service.McpRouterService;
import com.alibaba.nacos.api.exception.NacosException;
import jakarta.annotation.PostConstruct;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ai.document.MetadataMode;
Expand All @@ -38,6 +40,8 @@
import org.springframework.ai.tool.ToolCallbackProvider;
import org.springframework.ai.tool.method.MethodToolCallbackProvider;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
Expand All @@ -46,11 +50,15 @@
import java.util.Properties;

/**
* Register NacosMcpServiceDiscovery to McpServiceDiscoveryFactory.
*
* @author aias00
*/
@AutoConfiguration
@AutoConfigureAfter(McpServiceDiscoveryAutoConfiguration.class)
@EnableConfigurationProperties({ McpRouterProperties.class, NacosMcpProperties.class, McpServerProperties.class })
@ConditionalOnProperty(prefix = McpRouterProperties.CONFIG_PREFIX, name = "enabled", havingValue = "true",
matchIfMissing = false)
matchIfMissing = true)
public class NacosMcpRouterAutoConfiguration {

private static final Logger log = LoggerFactory.getLogger(NacosMcpRouterAutoConfiguration.class);
Expand All @@ -62,7 +70,7 @@ public class NacosMcpRouterAutoConfiguration {
@ConditionalOnMissingBean
public EmbeddingModel embeddingModel() {
if (apiKey == null || apiKey.isEmpty() || "default_api_key".equals(apiKey)) {
throw new IllegalArgumentException("Environment variable DASHSCOPE_API_KEY is not set.");
throw new IllegalArgumentException("Environment variable AI_DASHSCOPE_API_KEY is not set.");
}
DashScopeApi dashScopeApi = DashScopeApi.builder().apiKey(apiKey).build();

Expand All @@ -82,13 +90,11 @@ public NacosMcpOperationService nacosMcpOperationService(NacosMcpProperties naco
}
}

/**
* 配置 MCP 服务发现
*/
@Bean
@ConditionalOnMissingBean
public McpServiceDiscovery mcpServiceDiscovery(NacosMcpOperationService nacosMcpOperationService) {
return new NacosMcpServiceDiscovery(nacosMcpOperationService);
public NacosMcpServiceDiscoveryRegistrar nacosMcpServiceDiscoveryRegistrar(
McpServiceDiscoveryFactory discoveryFactory, NacosMcpOperationService nacosMcpOperationService) {
log.info("Creating Nacos MCP service discovery registrar");
return new NacosMcpServiceDiscoveryRegistrar(discoveryFactory, nacosMcpOperationService);
}

/**
Expand Down Expand Up @@ -138,4 +144,30 @@ public McpRouterWatcher mcpRouterWatcher(McpServiceDiscovery mcpServiceDiscovery
return new McpRouterWatcher(mcpServiceDiscovery, mcpServerVectorStore, mcpRouterProperties.getServiceNames());
}

/**
* Nacos MCP服务发现注册器
*/
public static class NacosMcpServiceDiscoveryRegistrar {

private final McpServiceDiscoveryFactory discoveryFactory;

private final NacosMcpOperationService nacosMcpOperationService;

public NacosMcpServiceDiscoveryRegistrar(McpServiceDiscoveryFactory discoveryFactory,
NacosMcpOperationService nacosMcpOperationService) {
this.discoveryFactory = discoveryFactory;
this.nacosMcpOperationService = nacosMcpOperationService;
log.info("Nacos MCP service discovery registrar constructor called");
}

@PostConstruct
public void init() {
log.info("Nacos MCP service discovery registrar initialized");
log.info("Registering Nacos MCP service discovery");
McpServiceDiscovery nacosDiscovery = new NacosMcpServiceDiscovery(nacosMcpOperationService);
discoveryFactory.registerDiscovery("nacos", nacosDiscovery);
}

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,13 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#

com.alibaba.cloud.ai.autoconfigure.mcp.router.McpServiceDiscoveryAutoConfiguration
com.alibaba.cloud.ai.autoconfigure.mcp.router.FileMcpRouterAutoConfiguration
com.alibaba.cloud.ai.autoconfigure.mcp.router.DbMcpRouterAutoConfiguration
com.alibaba.cloud.ai.autoconfigure.mcp.router.NacosMcpRouterAutoConfiguration

com.alibaba.cloud.ai.autoconfigure.mcp.gateway.core.McpGatewayServerAutoConfiguration
#com.alibaba.cloud.ai.autoconfigure.mcp.gateway.core.McpGatewaySseServerAutoConfiguration
#com.alibaba.cloud.ai.autoconfigure.mcp.gateway.core.McpGatewayStreamableServerAutoConfiguration
com.alibaba.cloud.ai.autoconfigure.mcp.gateway.nacos.NacosMcpGatewayAutoConfiguration

Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,7 @@ void testStreamImageResponse() {

/**
* Integration test for image processing with URL This test will only run if
* DASHSCOPE_API_KEY environment variable is set
* AI_DASHSCOPE_API_KEY environment variable is set
*/
@Test
@Tag("integration")
Expand Down
4 changes: 4 additions & 0 deletions spring-ai-alibaba-mcp/spring-ai-alibaba-mcp-router/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,10 @@
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
</dependency>
<!-- Test dependencies -->
<dependency>
<groupId>org.junit.jupiter</groupId>
Expand Down
Loading
Loading