Skip to content

Commit ad18b6d

Browse files
committed
support @DubboService annotation for service control, optimize logging and refactor package structure
1 parent 5cd8426 commit ad18b6d

File tree

13 files changed

+634
-113
lines changed

13 files changed

+634
-113
lines changed

dubbo-common/src/main/java/org/apache/dubbo/config/annotation/DubboService.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -354,4 +354,25 @@
354354
* The configuration supports multiple, which are separated by commas.Such as:<code>fastjson2,fastjson,hessian2</code>
355355
*/
356356
String preferSerialization() default "";
357+
358+
/**
359+
* Whether to expose this service as MCP tool, default value is false
360+
* Can be overridden by configuration property: dubbo.protocol.triple.rest.mcp.service.{interfaceName}.enabled
361+
*/
362+
boolean mcpEnabled() default false;
363+
364+
/**
365+
* MCP tool name for this service, if empty, use interfaceName + methodName as tool name
366+
*/
367+
String mcpToolName() default "";
368+
369+
/**
370+
* MCP tool description for this service
371+
*/
372+
String mcpDescription() default "";
373+
374+
/**
375+
* MCP tool tags for categorization
376+
*/
377+
String[] mcpTags() default {};
357378
}

dubbo-plugin/dubbo-mcp/src/main/java/org/apache/dubbo/mcp/server/McpConstant.java renamed to dubbo-plugin/dubbo-mcp/src/main/java/org/apache/dubbo/mcp/McpConstant.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
* See the License for the specific language governing permissions and
1515
* limitations under the License.
1616
*/
17-
package org.apache.dubbo.mcp.server;
17+
package org.apache.dubbo.mcp;
1818

1919
public interface McpConstant {
2020

@@ -24,6 +24,18 @@ public interface McpConstant {
2424
String SETTINGS_MCP_PATHS_SSE = "dubbo.protocol.triple.rest.mcp.path.sse";
2525
String SETTINGS_MCP_PATHS_MESSAGE = "dubbo.protocol.triple.rest.mcp.path.message";
2626

27+
// MCP 服务控制相关配置
28+
String SETTINGS_MCP_SERVICE_PREFIX = "dubbo.protocol.triple.rest.mcp.service";
29+
String SETTINGS_MCP_SERVICE_ENABLED_SUFFIX = "enabled";
30+
String SETTINGS_MCP_SERVICE_NAME_SUFFIX = "tool-name";
31+
String SETTINGS_MCP_SERVICE_DESCRIPTION_SUFFIX = "description";
32+
String SETTINGS_MCP_SERVICE_TAGS_SUFFIX = "tags";
33+
34+
// 全局默认配置
35+
String SETTINGS_MCP_DEFAULT_ENABLED = "dubbo.protocol.triple.rest.mcp.default.enabled";
36+
String SETTINGS_MCP_INCLUDE_PATTERNS = "dubbo.protocol.triple.rest.mcp.include.patterns";
37+
String SETTINGS_MCP_EXCLUDE_PATTERNS = "dubbo.protocol.triple.rest.mcp.exclude.patterns";
38+
2739
String MCP_SERVICE_PROTOCOL = "mcp-service-protocol";
2840
String MCP_SERVICE_PORT = "mcp-service-port";
2941
}
Lines changed: 27 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
* See the License for the specific language governing permissions and
1515
* limitations under the License.
1616
*/
17-
package org.apache.dubbo.mcp.server.registry;
17+
package org.apache.dubbo.mcp.core;
1818

1919
import org.apache.dubbo.common.config.Configuration;
2020
import org.apache.dubbo.common.config.ConfigurationUtils;
@@ -29,11 +29,12 @@
2929
import org.apache.dubbo.config.ProtocolConfig;
3030
import org.apache.dubbo.config.ServiceConfig;
3131
import org.apache.dubbo.config.bootstrap.builders.InternalServiceConfigBuilder;
32-
import org.apache.dubbo.mcp.server.McpConstant;
33-
import org.apache.dubbo.mcp.server.McpSseService;
34-
import org.apache.dubbo.mcp.server.McpSseServiceImpl;
35-
import org.apache.dubbo.mcp.server.generic.DubboMcpGenericCaller;
36-
import org.apache.dubbo.mcp.server.transport.DubboMcpSseTransportProvider;
32+
33+
import org.apache.dubbo.mcp.McpConstant;
34+
import org.apache.dubbo.mcp.tool.DubboMcpGenericCaller;
35+
import org.apache.dubbo.mcp.tool.DubboOpenApiToolConverter;
36+
import org.apache.dubbo.mcp.tool.DubboServiceToolRegistry;
37+
import org.apache.dubbo.mcp.transport.DubboMcpSseTransportProvider;
3738
import org.apache.dubbo.rpc.model.ApplicationModel;
3839
import org.apache.dubbo.rpc.model.FrameworkModel;
3940
import org.apache.dubbo.rpc.model.ProviderModel;
@@ -54,6 +55,7 @@ public class McpApplicationDeployListener implements ApplicationDeployListener {
5455
private static final ErrorTypeAwareLogger logger =
5556
LoggerFactory.getErrorTypeAwareLogger(McpApplicationDeployListener.class);
5657
private DubboServiceToolRegistry toolRegistry;
58+
private McpServiceFilter mcpServiceFilter;
5759
private boolean mcpEnable = true;
5860

5961
private volatile ServiceConfig<McpSseService> serviceConfig;
@@ -74,14 +76,15 @@ public static DubboMcpSseTransportProvider getDubboMcpSseTransportProvider() {
7476
public void onStarted(ApplicationModel applicationModel) {
7577
Configuration globalConf = ConfigurationUtils.getGlobalConfiguration(ApplicationModel.defaultModel());
7678
mcpEnable = globalConf.getBoolean(McpConstant.SETTINGS_MCP_ENABLE, true);
77-
if (mcpEnable) {
78-
logger.debug("McpApplicationDeployListener: MCP service is enabled.");
79-
} else {
80-
logger.debug("McpApplicationDeployListener: MCP service is disabled. Skipping initialization.");
79+
if (!mcpEnable) {
80+
logger.info("MCP service is disabled, skipping initialization");
8181
return;
8282
}
8383
try {
84-
logger.info("McpApplicationDeployListener: Application started. Initializing MCP server and tools...");
84+
logger.info("Initializing MCP server and tools");
85+
86+
// Initialize service filter
87+
mcpServiceFilter = new McpServiceFilter(applicationModel);
8588

8689
dubboMcpSseTransportProvider = new DubboMcpSseTransportProvider(new ObjectMapper());
8790
McpSchema.ServerCapabilities.ToolCapabilities toolCapabilities =
@@ -104,15 +107,20 @@ public void onStarted(ApplicationModel applicationModel) {
104107

105108
Collection<ProviderModel> providerModels =
106109
applicationModel.getApplicationServiceRepository().allProviderModels();
107-
logger.info("Found " + providerModels.size() + " provider models. Starting tool registration...");
110+
111+
int registeredCount = 0;
108112
for (ProviderModel pm : providerModels) {
109-
logger.info("Processing ProviderModel: " + pm.getServiceKey() + ", module: "
110-
+ pm.getModuleModel().getDesc());
111-
toolRegistry.registerService(pm);
113+
// Check if service should be exposed as MCP tool
114+
if (mcpServiceFilter.shouldExposeAsMcpTool(pm)) {
115+
// Get MCP tool configuration
116+
McpServiceFilter.McpToolConfig toolConfig = mcpServiceFilter.getMcpToolConfig(pm);
117+
toolRegistry.registerService(pm, toolConfig);
118+
registeredCount++;
119+
}
112120
}
121+
113122
exportMcpService(applicationModel);
114-
logger.info("MCP service initialization complete. Registered " + toolRegistry.getRegisteredToolCount()
115-
+ " tools.");
123+
logger.info("MCP server initialized successfully, {} tools registered", registeredCount);
116124
} catch (Exception e) {
117125
logger.error(
118126
LoggerCodeConstants.COMMON_UNEXPECTED_EXCEPTION,
@@ -126,7 +134,7 @@ public void onStarted(ApplicationModel applicationModel) {
126134
@Override
127135
public void onStopping(ApplicationModel applicationModel) {
128136
if (toolRegistry != null) {
129-
logger.info("MCP service stopping. Clearing tool registry...");
137+
logger.info("MCP server stopping, clearing tool registry");
130138
toolRegistry.clearRegistry();
131139
}
132140
}
@@ -161,7 +169,7 @@ private void exportMcpService(ApplicationModel applicationModel) {
161169
.version(V1)
162170
.build();
163171
serviceConfig.export();
164-
logger.info("The MCP service exports urls : " + serviceConfig.getExportedUrls());
172+
logger.info("MCP service exported on: {}", serviceConfig.getExportedUrls());
165173
}
166174

167175
/**
Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package org.apache.dubbo.mcp.core;
18+
import org.apache.dubbo.common.config.Configuration;
19+
import org.apache.dubbo.common.config.ConfigurationUtils;
20+
import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
21+
import org.apache.dubbo.common.logger.LoggerFactory;
22+
import org.apache.dubbo.common.utils.StringUtils;
23+
import org.apache.dubbo.config.annotation.DubboService;
24+
import org.apache.dubbo.mcp.McpConstant;
25+
import org.apache.dubbo.rpc.model.ApplicationModel;
26+
import org.apache.dubbo.rpc.model.ProviderModel;
27+
28+
import java.util.Arrays;
29+
import java.util.List;
30+
import java.util.regex.Pattern;
31+
32+
public class McpServiceFilter {
33+
34+
private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(McpServiceFilter.class);
35+
36+
private final Configuration configuration;
37+
private final Pattern[] includePatterns;
38+
private final Pattern[] excludePatterns;
39+
private final boolean defaultEnabled;
40+
41+
public McpServiceFilter(ApplicationModel applicationModel) {
42+
this.configuration = ConfigurationUtils.getGlobalConfiguration(applicationModel);
43+
this.defaultEnabled = configuration.getBoolean(McpConstant.SETTINGS_MCP_DEFAULT_ENABLED, true);
44+
45+
// 解析包含和排除模式
46+
String includeStr = configuration.getString(McpConstant.SETTINGS_MCP_INCLUDE_PATTERNS, "");
47+
String excludeStr = configuration.getString(McpConstant.SETTINGS_MCP_EXCLUDE_PATTERNS, "");
48+
49+
this.includePatterns = parsePatterns(includeStr);
50+
this.excludePatterns = parsePatterns(excludeStr);
51+
52+
logger.debug(
53+
"MCP service filter initialized: defaultEnabled={}, includePatterns={}, excludePatterns={}",
54+
defaultEnabled,
55+
includeStr,
56+
excludeStr);
57+
}
58+
59+
/**
60+
* Check if service should be exposed as MCP tool
61+
*/
62+
public boolean shouldExposeAsMcpTool(ProviderModel providerModel) {
63+
String interfaceName = providerModel.getServiceModel().getInterfaceName();
64+
65+
// 1. Check exclude patterns (highest priority)
66+
if (isMatchedByPatterns(interfaceName, excludePatterns)) {
67+
return false;
68+
}
69+
70+
// 2. Check annotation configuration
71+
Object serviceBean = providerModel.getServiceInstance();
72+
if (serviceBean != null) {
73+
DubboService dubboService = serviceBean.getClass().getAnnotation(DubboService.class);
74+
if (dubboService != null && dubboService.mcpEnabled()) {
75+
return true;
76+
}
77+
}
78+
79+
// 3. Check specific service configuration
80+
String serviceSpecificKey = McpConstant.SETTINGS_MCP_SERVICE_PREFIX + "." + interfaceName + "."
81+
+ McpConstant.SETTINGS_MCP_SERVICE_ENABLED_SUFFIX;
82+
Boolean configEnabled = configuration.getBoolean(serviceSpecificKey, (Boolean) null);
83+
if (configEnabled != null) {
84+
return configEnabled;
85+
}
86+
87+
// 4. Check include patterns
88+
if (includePatterns.length > 0) {
89+
// If include patterns are defined, only services matching them should be included
90+
return isMatchedByPatterns(interfaceName, includePatterns);
91+
}
92+
93+
// 5. Use default configuration
94+
return defaultEnabled;
95+
}
96+
97+
/**
98+
* Get MCP tool configuration for service
99+
*/
100+
public McpToolConfig getMcpToolConfig(ProviderModel providerModel) {
101+
String interfaceName = providerModel.getServiceModel().getInterfaceName();
102+
McpToolConfig config = new McpToolConfig();
103+
104+
// Get configuration from annotation
105+
Object serviceBean = providerModel.getServiceInstance();
106+
if (serviceBean != null) {
107+
DubboService dubboService = serviceBean.getClass().getAnnotation(DubboService.class);
108+
if (dubboService != null) {
109+
config.setToolName(dubboService.mcpToolName());
110+
config.setDescription(dubboService.mcpDescription());
111+
config.setTags(Arrays.asList(dubboService.mcpTags()));
112+
}
113+
}
114+
115+
// Get configuration from config file (higher priority)
116+
String servicePrefix = McpConstant.SETTINGS_MCP_SERVICE_PREFIX + "." + interfaceName + ".";
117+
118+
String configToolName = configuration.getString(servicePrefix + McpConstant.SETTINGS_MCP_SERVICE_NAME_SUFFIX);
119+
if (StringUtils.isNotEmpty(configToolName)) {
120+
config.setToolName(configToolName);
121+
}
122+
123+
String configDescription =
124+
configuration.getString(servicePrefix + McpConstant.SETTINGS_MCP_SERVICE_DESCRIPTION_SUFFIX);
125+
if (StringUtils.isNotEmpty(configDescription)) {
126+
config.setDescription(configDescription);
127+
}
128+
129+
String configTags = configuration.getString(servicePrefix + McpConstant.SETTINGS_MCP_SERVICE_TAGS_SUFFIX);
130+
if (StringUtils.isNotEmpty(configTags)) {
131+
config.setTags(Arrays.asList(configTags.split(",")));
132+
}
133+
134+
return config;
135+
}
136+
137+
private Pattern[] parsePatterns(String patternStr) {
138+
if (StringUtils.isEmpty(patternStr)) {
139+
return new Pattern[0];
140+
}
141+
142+
return Arrays.stream(patternStr.split(","))
143+
.map(String::trim)
144+
.filter(StringUtils::isNotEmpty)
145+
.map(pattern -> Pattern.compile(pattern.replace("*", ".*")))
146+
.toArray(Pattern[]::new);
147+
}
148+
149+
private boolean isMatchedByPatterns(String text, Pattern[] patterns) {
150+
for (Pattern pattern : patterns) {
151+
if (pattern.matcher(text).matches()) {
152+
return true;
153+
}
154+
}
155+
return false;
156+
}
157+
158+
/**
159+
* MCP tool configuration
160+
*/
161+
public static class McpToolConfig {
162+
private String toolName;
163+
private String description;
164+
private List<String> tags;
165+
166+
public String getToolName() {
167+
return toolName;
168+
}
169+
170+
public void setToolName(String toolName) {
171+
this.toolName = toolName;
172+
}
173+
174+
public String getDescription() {
175+
return description;
176+
}
177+
178+
public void setDescription(String description) {
179+
this.description = description;
180+
}
181+
182+
public List<String> getTags() {
183+
return tags;
184+
}
185+
186+
public void setTags(List<String> tags) {
187+
this.tags = tags;
188+
}
189+
}
190+
}

dubbo-plugin/dubbo-mcp/src/main/java/org/apache/dubbo/mcp/server/McpSseService.java renamed to dubbo-plugin/dubbo-mcp/src/main/java/org/apache/dubbo/mcp/core/McpSseService.java

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,14 @@
1414
* See the License for the specific language governing permissions and
1515
* limitations under the License.
1616
*/
17-
package org.apache.dubbo.mcp.server;
18-
17+
package org.apache.dubbo.mcp.core;
1918
import org.apache.dubbo.common.stream.StreamObserver;
2019
import org.apache.dubbo.remoting.http12.HttpMethods;
2120
import org.apache.dubbo.remoting.http12.message.ServerSentEvent;
2221
import org.apache.dubbo.remoting.http12.rest.Mapping;
2322

24-
import static org.apache.dubbo.mcp.server.McpConstant.SETTINGS_MCP_PATHS_MESSAGE;
25-
import static org.apache.dubbo.mcp.server.McpConstant.SETTINGS_MCP_PATHS_SSE;
23+
import static org.apache.dubbo.mcp.McpConstant.SETTINGS_MCP_PATHS_MESSAGE;
24+
import static org.apache.dubbo.mcp.McpConstant.SETTINGS_MCP_PATHS_SSE;
2625

2726
@Mapping("")
2827
public interface McpSseService {

dubbo-plugin/dubbo-mcp/src/main/java/org/apache/dubbo/mcp/server/McpSseServiceImpl.java renamed to dubbo-plugin/dubbo-mcp/src/main/java/org/apache/dubbo/mcp/core/McpSseServiceImpl.java

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,10 @@
1414
* See the License for the specific language governing permissions and
1515
* limitations under the License.
1616
*/
17-
package org.apache.dubbo.mcp.server;
18-
17+
package org.apache.dubbo.mcp.core;
1918
import org.apache.dubbo.common.resource.Disposable;
2019
import org.apache.dubbo.common.stream.StreamObserver;
21-
import org.apache.dubbo.mcp.server.registry.McpApplicationDeployListener;
22-
import org.apache.dubbo.mcp.server.transport.DubboMcpSseTransportProvider;
20+
import org.apache.dubbo.mcp.transport.DubboMcpSseTransportProvider;
2321
import org.apache.dubbo.remoting.http12.message.ServerSentEvent;
2422

2523
public class McpSseServiceImpl implements McpSseService, Disposable {

0 commit comments

Comments
 (0)