AlbumenJ commented on code in PR #15406:
URL: https://github.com/apache/dubbo/pull/15406#discussion_r2108081336


##########
dubbo-plugin/dubbo-mcp/pom.xml:
##########
@@ -0,0 +1,100 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You 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
+
+      http://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.
+  -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"; 
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/xsd/maven-4.0.0.xsd";>
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.apache.dubbo</groupId>
+    <artifactId>dubbo-parent</artifactId>
+    <version>${revision}</version>
+    <relativePath>../../pom.xml</relativePath>
+  </parent>
+
+  <artifactId>dubbo-mcp</artifactId>
+  <properties>
+    <mcp.version>0.9.0</mcp.version>
+    <skip_maven_deploy>false</skip_maven_deploy>
+    <guava.version>33.2.1-jre</guava.version>

Review Comment:
   Why import guava here



##########
dubbo-plugin/dubbo-mcp/src/main/java/org/apache/dubbo/mcp/core/McpApplicationDeployListener.java:
##########
@@ -0,0 +1,197 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ *     http://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 org.apache.dubbo.mcp.core;
+
+import org.apache.dubbo.common.config.Configuration;
+import org.apache.dubbo.common.config.ConfigurationUtils;
+import org.apache.dubbo.common.constants.CommonConstants;
+import org.apache.dubbo.common.constants.LoggerCodeConstants;
+import org.apache.dubbo.common.deploy.ApplicationDeployListener;
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
+import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.common.threadpool.manager.FrameworkExecutorRepository;
+import org.apache.dubbo.common.utils.CollectionUtils;
+import org.apache.dubbo.common.utils.NetUtils;
+import org.apache.dubbo.config.ProtocolConfig;
+import org.apache.dubbo.config.ServiceConfig;
+import org.apache.dubbo.config.bootstrap.builders.InternalServiceConfigBuilder;
+import org.apache.dubbo.mcp.McpConstant;
+import org.apache.dubbo.mcp.tool.DubboMcpGenericCaller;
+import org.apache.dubbo.mcp.tool.DubboOpenApiToolConverter;
+import org.apache.dubbo.mcp.tool.DubboServiceToolRegistry;
+import org.apache.dubbo.mcp.transport.DubboMcpSseTransportProvider;
+import org.apache.dubbo.rpc.model.ApplicationModel;
+import org.apache.dubbo.rpc.model.FrameworkModel;
+import org.apache.dubbo.rpc.model.ProviderModel;
+import org.apache.dubbo.rpc.protocol.tri.rest.openapi.DefaultOpenAPIService;
+
+import java.util.Collection;
+import java.util.concurrent.ExecutorService;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import io.modelcontextprotocol.server.McpAsyncServer;
+import io.modelcontextprotocol.server.McpServer;
+import io.modelcontextprotocol.spec.McpSchema;
+
+import static org.apache.dubbo.metadata.util.MetadataServiceVersionUtils.V1;
+
+public class McpApplicationDeployListener implements ApplicationDeployListener 
{
+
+    private static final ErrorTypeAwareLogger logger =
+            
LoggerFactory.getErrorTypeAwareLogger(McpApplicationDeployListener.class);
+    private DubboServiceToolRegistry toolRegistry;
+    private McpServiceFilter mcpServiceFilter;
+    private boolean mcpEnable = true;
+
+    private volatile ServiceConfig<McpSseService> serviceConfig;
+
+    private static DubboMcpSseTransportProvider dubboMcpSseTransportProvider;
+
+    @Override
+    public void onInitialize(ApplicationModel scopeModel) {}
+
+    @Override
+    public void onStarting(ApplicationModel applicationModel) {}
+
+    public static DubboMcpSseTransportProvider 
getDubboMcpSseTransportProvider() {
+        return dubboMcpSseTransportProvider;
+    }
+
+    @Override
+    public void onStarted(ApplicationModel applicationModel) {
+        Configuration globalConf = 
ConfigurationUtils.getGlobalConfiguration(ApplicationModel.defaultModel());

Review Comment:
   Prevent using `ApplicationModel.defaultModel()`



##########
dubbo-plugin/dubbo-mcp/src/main/java/org/apache/dubbo/mcp/core/McpServiceFilter.java:
##########
@@ -0,0 +1,191 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ *     http://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 org.apache.dubbo.mcp.core;
+
+import org.apache.dubbo.common.config.Configuration;
+import org.apache.dubbo.common.config.ConfigurationUtils;
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
+import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.common.utils.StringUtils;
+import org.apache.dubbo.config.annotation.DubboService;
+import org.apache.dubbo.mcp.McpConstant;
+import org.apache.dubbo.rpc.model.ApplicationModel;
+import org.apache.dubbo.rpc.model.ProviderModel;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.regex.Pattern;
+
+public class McpServiceFilter {
+
+    private static final ErrorTypeAwareLogger logger = 
LoggerFactory.getErrorTypeAwareLogger(McpServiceFilter.class);
+
+    private final Configuration configuration;
+    private final Pattern[] includePatterns;
+    private final Pattern[] excludePatterns;
+    private final boolean defaultEnabled;
+
+    public McpServiceFilter(ApplicationModel applicationModel) {
+        this.configuration = 
ConfigurationUtils.getGlobalConfiguration(applicationModel);
+        this.defaultEnabled = 
configuration.getBoolean(McpConstant.SETTINGS_MCP_DEFAULT_ENABLED, true);
+
+        // Parse include and exclude patterns
+        String includeStr = 
configuration.getString(McpConstant.SETTINGS_MCP_INCLUDE_PATTERNS, "");
+        String excludeStr = 
configuration.getString(McpConstant.SETTINGS_MCP_EXCLUDE_PATTERNS, "");
+
+        this.includePatterns = parsePatterns(includeStr);
+        this.excludePatterns = parsePatterns(excludeStr);
+
+        logger.debug(
+                "MCP service filter initialized: defaultEnabled={}, 
includePatterns={}, excludePatterns={}",
+                defaultEnabled,
+                includeStr,
+                excludeStr);
+    }
+
+    /**
+     * Check if service should be exposed as MCP tool
+     */
+    public boolean shouldExposeAsMcpTool(ProviderModel providerModel) {
+        String interfaceName = 
providerModel.getServiceModel().getInterfaceName();
+
+        // 1. Check exclude patterns (the highest priority)
+        if (isMatchedByPatterns(interfaceName, excludePatterns)) {
+            return false;
+        }
+
+        // 2. Check annotation configuration
+        Object serviceBean = providerModel.getServiceInstance();
+        if (serviceBean != null) {
+            DubboService dubboService = 
serviceBean.getClass().getAnnotation(DubboService.class);
+            if (dubboService != null && dubboService.mcpEnabled()) {
+                return true;
+            }
+        }

Review Comment:
   Should get properties from configs or URL.



##########
dubbo-plugin/dubbo-mcp/src/main/java/org/apache/dubbo/mcp/core/McpApplicationDeployListener.java:
##########
@@ -0,0 +1,197 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ *     http://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 org.apache.dubbo.mcp.core;
+
+import org.apache.dubbo.common.config.Configuration;
+import org.apache.dubbo.common.config.ConfigurationUtils;
+import org.apache.dubbo.common.constants.CommonConstants;
+import org.apache.dubbo.common.constants.LoggerCodeConstants;
+import org.apache.dubbo.common.deploy.ApplicationDeployListener;
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
+import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.common.threadpool.manager.FrameworkExecutorRepository;
+import org.apache.dubbo.common.utils.CollectionUtils;
+import org.apache.dubbo.common.utils.NetUtils;
+import org.apache.dubbo.config.ProtocolConfig;
+import org.apache.dubbo.config.ServiceConfig;
+import org.apache.dubbo.config.bootstrap.builders.InternalServiceConfigBuilder;
+import org.apache.dubbo.mcp.McpConstant;
+import org.apache.dubbo.mcp.tool.DubboMcpGenericCaller;
+import org.apache.dubbo.mcp.tool.DubboOpenApiToolConverter;
+import org.apache.dubbo.mcp.tool.DubboServiceToolRegistry;
+import org.apache.dubbo.mcp.transport.DubboMcpSseTransportProvider;
+import org.apache.dubbo.rpc.model.ApplicationModel;
+import org.apache.dubbo.rpc.model.FrameworkModel;
+import org.apache.dubbo.rpc.model.ProviderModel;
+import org.apache.dubbo.rpc.protocol.tri.rest.openapi.DefaultOpenAPIService;
+
+import java.util.Collection;
+import java.util.concurrent.ExecutorService;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import io.modelcontextprotocol.server.McpAsyncServer;
+import io.modelcontextprotocol.server.McpServer;
+import io.modelcontextprotocol.spec.McpSchema;
+
+import static org.apache.dubbo.metadata.util.MetadataServiceVersionUtils.V1;
+
+public class McpApplicationDeployListener implements ApplicationDeployListener 
{
+
+    private static final ErrorTypeAwareLogger logger =
+            
LoggerFactory.getErrorTypeAwareLogger(McpApplicationDeployListener.class);
+    private DubboServiceToolRegistry toolRegistry;
+    private McpServiceFilter mcpServiceFilter;
+    private boolean mcpEnable = true;
+
+    private volatile ServiceConfig<McpSseService> serviceConfig;
+
+    private static DubboMcpSseTransportProvider dubboMcpSseTransportProvider;
+
+    @Override
+    public void onInitialize(ApplicationModel scopeModel) {}
+
+    @Override
+    public void onStarting(ApplicationModel applicationModel) {}
+
+    public static DubboMcpSseTransportProvider 
getDubboMcpSseTransportProvider() {
+        return dubboMcpSseTransportProvider;
+    }
+
+    @Override
+    public void onStarted(ApplicationModel applicationModel) {
+        Configuration globalConf = 
ConfigurationUtils.getGlobalConfiguration(ApplicationModel.defaultModel());
+        mcpEnable = globalConf.getBoolean(McpConstant.SETTINGS_MCP_ENABLE, 
true);
+        if (!mcpEnable) {
+            logger.info("MCP service is disabled, skipping initialization");
+            return;
+        }
+        try {
+            logger.info("Initializing MCP server and tools");
+
+            // Initialize service filter
+            mcpServiceFilter = new McpServiceFilter(applicationModel);
+
+            dubboMcpSseTransportProvider = new 
DubboMcpSseTransportProvider(new ObjectMapper());
+            McpSchema.ServerCapabilities.ToolCapabilities toolCapabilities =
+                    new McpSchema.ServerCapabilities.ToolCapabilities(true);
+            McpSchema.ServerCapabilities serverCapabilities =
+                    new McpSchema.ServerCapabilities(null, null, null, null, 
toolCapabilities);
+
+            McpAsyncServer mcpAsyncServer = 
McpServer.async(dubboMcpSseTransportProvider)
+                    .capabilities(serverCapabilities)
+                    .build();
+
+            FrameworkModel frameworkModel = 
applicationModel.getFrameworkModel();
+            DefaultOpenAPIService defaultOpenAPIService = new 
DefaultOpenAPIService(frameworkModel);
+
+            DubboOpenApiToolConverter toolConverter = new 
DubboOpenApiToolConverter(defaultOpenAPIService);
+
+            DubboMcpGenericCaller genericCaller = new 
DubboMcpGenericCaller(applicationModel);
+
+            toolRegistry = new DubboServiceToolRegistry(mcpAsyncServer, 
toolConverter, genericCaller);
+
+            Collection<ProviderModel> providerModels =
+                    
applicationModel.getApplicationServiceRepository().allProviderModels();
+
+            int registeredCount = 0;
+            for (ProviderModel pm : providerModels) {
+                // Check if service should be exposed as MCP tool
+                if (mcpServiceFilter.shouldExposeAsMcpTool(pm)) {
+                    // Get MCP tool configuration
+                    McpServiceFilter.McpToolConfig toolConfig = 
mcpServiceFilter.getMcpToolConfig(pm);
+                    toolRegistry.registerService(pm, toolConfig);
+                    registeredCount++;
+                }
+            }

Review Comment:
   How to manage dynamic exported services



##########
dubbo-plugin/dubbo-mcp/src/main/java/org/apache/dubbo/mcp/tool/DubboMcpGenericCaller.java:
##########
@@ -0,0 +1,133 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ *     http://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 org.apache.dubbo.mcp.tool;
+
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
+import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.config.ApplicationConfig;
+import org.apache.dubbo.config.ReferenceConfig;
+import org.apache.dubbo.rpc.model.ApplicationModel;
+import org.apache.dubbo.rpc.service.GenericService;
+
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import static 
org.apache.dubbo.common.constants.LoggerCodeConstants.COMMON_UNEXPECTED_EXCEPTION;
+
+public class DubboMcpGenericCaller {
+
+    private static final ErrorTypeAwareLogger logger =
+            LoggerFactory.getErrorTypeAwareLogger(DubboMcpGenericCaller.class);
+
+    private final ApplicationConfig applicationConfig;
+
+    private final Map<String, GenericService> serviceCache = new 
ConcurrentHashMap<>();
+
+    public DubboMcpGenericCaller(ApplicationModel applicationModel) {
+        if (applicationModel == null) {
+            logger.error(
+                    COMMON_UNEXPECTED_EXCEPTION, "", "", "ApplicationModel 
cannot be null for DubboMcpGenericCaller.");
+            throw new IllegalArgumentException("ApplicationModel cannot be 
null.");
+        }
+        this.applicationConfig = applicationModel.getCurrentConfig();
+        if (this.applicationConfig == null) {
+
+            String errMsg = "ApplicationConfig is null in the provided 
ApplicationModel. Application Name: "
+                    + (applicationModel.getApplicationName() != null ? 
applicationModel.getApplicationName() : "N/A");
+            logger.error(COMMON_UNEXPECTED_EXCEPTION, "", "", errMsg);
+            throw new IllegalStateException(errMsg);
+        }
+    }
+
+    public Object execute(
+            String interfaceName,
+            String methodName,
+            List<String> orderedJavaParameterNames,
+            Class<?>[] parameterJavaTypes,
+            Map<String, Object> mcpProvidedParameters,
+            String group,
+            String version) {
+        String cacheKey = interfaceName + ":" + (group == null ? "" : group) + 
":" + (version == null ? "" : version);
+        GenericService genericService = serviceCache.get(cacheKey);
+        if (genericService == null) {
+            ReferenceConfig<GenericService> reference = new 
ReferenceConfig<>();
+            reference.setApplication(this.applicationConfig);
+            reference.setInterface(interfaceName);
+            reference.setGeneric("true"); // Defaults to 'bean' or 'true' for 
POJO generalization.
+
+            // If no registries are configured, assume local JVM call is 
intended or possible.
+            if (this.applicationConfig.getRegistries() == null
+                    || this.applicationConfig.getRegistries().isEmpty()) {
+                reference.setScope("local");
+            }

Review Comment:
   Should usimg injvm invoke in MCP



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to