Copilot commented on code in PR #6163: URL: https://github.com/apache/shenyu/pull/6163#discussion_r2357460697
########## shenyu-client/shenyu-client-mcp/src/main/java/org/apache/shenyu/client/mcp/common/constants/OpenApiConstants.java: ########## @@ -0,0 +1,124 @@ +/* + * 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.shenyu.client.mcp.common.constants; + +/** + * openApi constants. + */ +public class OpenApiConstants { + + /** + * the version of openApi key. + */ + public static final String OPEN_API_VERSION_KEY = "openapi"; + + /** + * the info of openApi key. + */ + public static final String OPEN_API_INFO_KEY = "info"; + + /** + * the title of info key. + */ + public static final String OPEN_API_INFO_TITLE_KEY = "title"; + + /** + * the description of info key. + */ + public static final String OPEN_API_INFO_DESCRIPTION_KEY = "description"; + + /** + * the version of info key. + */ + public static final String OPEN_API_INFO_VERSION_KEY = "version"; + + /** + * the server of openApi key. + */ + public static final String OPEN_API_SERVER_KEY = "server"; + + /** + * the url of openApi server. + */ + public static final String OPEN_API_SERVER_URL_KEY = "url"; + + /** + * the path of openApi key. + */ + public static final String OPEN_API_PATH_KEY = "paths"; + + /** + * the summary of method key. + */ + public static final String OPEN_API_PATH_PATH_METHOD_SUMMARY_KEY = "summary"; + + /** + * the description of method key. + */ + public static final String OPEN_API_PATH_PATH_METHOD_DESCRIPTION_KEY = "description"; + + /** + * the operationId of method key. + */ + public static final String OPEN_API_PATH_PATH_METHOD_OPERATION_ID_KEY = "operationId"; + + /** + * the tag of method key. + */ + public static final String OPEN_API_PATH_PATH_METHOD_TAG_KEY = "tag"; + + /** + * the parameters of method key. + */ + public static final String OPEN_API_PATH_PATH_METHOD_PARAMETERS_KEY = "parameters"; + + /** + * the name of parameter key. + */ + public static final String OPEN_API_PATH_PATH_METHOD_PARAMETERS_NAME_KEY = "name"; + + /** + * the location of parameter key. + */ + public static final String OPEN_API_PATH_PATH_METHOD_PARAMETERS_IN_KEY = "in"; + + /** + * the description of parameter key. + */ + public static final String OPEN_API_PATH_PATH_METHOD_PARAMETERS_DESCRIPTION_KEY = "description"; + + /** + * the required of parameter key. + */ + public static final String OPEN_API_PATH_PATH_METHOD_PARAMETERS_REQUIRED_KEY = "description"; Review Comment: The constant value should be 'required' instead of 'description' as this represents the required field of a parameter, not its description. ```suggestion public static final String OPEN_API_PATH_PATH_METHOD_PARAMETERS_REQUIRED_KEY = "required"; ``` ########## shenyu-spring-boot-starter/shenyu-spring-boot-starter-client/shenyu-spring-boot-starter-client-mcp/src/main/java/org/apache/shenyu/springboot/starter/client/mcp/ShenyuMcpClientConfiguration.java: ########## @@ -0,0 +1,56 @@ +/* + * 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.shenyu.springboot.starter.client.mcp; + +import org.apache.shenyu.client.mcp.McpServiceEventListener; +import org.apache.shenyu.common.utils.VersionUtils; +import org.apache.shenyu.register.client.api.ShenyuClientRegisterRepository; +import org.apache.shenyu.register.common.config.ShenyuClientConfig; +import org.apache.shenyu.springboot.starter.client.common.config.ShenyuClientCommonBeanConfiguration; +import org.springframework.boot.autoconfigure.ImportAutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * The type shenyu apache mcp client configuration. + */ + +@Configuration +@ImportAutoConfiguration(ShenyuClientCommonBeanConfiguration.class) +@ConditionalOnProperty(value = "shenyu.register.enabled", matchIfMissing = true, havingValue = "true") +public class ShenyuMcpClientConfiguration { + + static { + VersionUtils.checkDuplicate(ShenyuMcpClientConfiguration.class); + } + + /** + * Apache mcp service bean listener. + * + * @param clientConfig the client config + * @param shenyuClientRegisterRepository the shenyu client register repository + * @return the apache dubbo service bean listener Review Comment: The return type documentation incorrectly mentions 'apache dubbo service bean listener' but should reference 'MCP service event listener' since this is for MCP, not Dubbo. ```suggestion * @return the MCP service event listener ``` ########## shenyu-client/shenyu-client-mcp/src/main/java/org/apache/shenyu/client/mcp/McpServiceEventListener.java: ########## @@ -0,0 +1,256 @@ +/* + * 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.shenyu.client.mcp; + +import com.google.gson.JsonObject; +import io.swagger.v3.oas.annotations.servers.Server; +import org.apache.commons.lang3.StringUtils; +import org.apache.shenyu.client.core.client.AbstractContextRefreshedEventListener; +import org.apache.shenyu.client.mcp.common.annotation.ShenyuMcpClient; +import org.apache.shenyu.client.mcp.generator.McpOpenApiGenerator; +import org.apache.shenyu.client.mcp.generator.McpToolsRegisterDTOGenerator; +import org.apache.shenyu.common.enums.ApiHttpMethodEnum; +import org.apache.shenyu.common.enums.RpcTypeEnum; +import org.apache.shenyu.register.client.api.ShenyuClientRegisterRepository; +import org.apache.shenyu.register.common.config.ShenyuClientConfig; +import org.apache.shenyu.register.common.dto.McpToolsRegisterDTO; +import org.apache.shenyu.register.common.dto.MetaDataRegisterDTO; +import org.apache.shenyu.register.common.dto.URIRegisterDTO; +import org.javatuples.Sextet; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.aop.support.AopUtils; +import org.springframework.context.ApplicationContext; +import org.springframework.core.annotation.AnnotatedElementUtils; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; + +/** + * Mcp service event Listener. + */ +public class McpServiceEventListener extends AbstractContextRefreshedEventListener<Object, ShenyuMcpClient> { + + private static final Logger log = LoggerFactory.getLogger(McpServiceEventListener.class); + + /** + * Instantiates a new context refreshed event listener. + * + * @param clientConfig the shenyu client config + * @param shenyuClientRegisterRepository the shenyuClientRegisterRepository + */ + public McpServiceEventListener(final ShenyuClientConfig clientConfig, final ShenyuClientRegisterRepository shenyuClientRegisterRepository) { + super(clientConfig, shenyuClientRegisterRepository); + } + + @Override + protected Sextet<String[], String, String, ApiHttpMethodEnum[], RpcTypeEnum, String> buildApiDocSextet(final Method method, final Annotation annotation, final Map<String, Object> beans) { + return null; + } + + @Override + protected Map<String, Object> getBeans(final ApplicationContext context) { + Map<String, Object> controllerBeans = context.getBeansWithAnnotation(Controller.class); + return controllerBeans.entrySet().stream() + .filter(entry -> { + Object bean = entry.getValue(); + + Class<?> targetClass = AopUtils.getTargetClass(bean); + + return targetClass.isAnnotationPresent(ShenyuMcpClient.class); + }) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + } + + @Override + protected URIRegisterDTO buildURIRegisterDTO(final ApplicationContext context, final Map<String, Object> beans, final String namespaceId) { + return null; + } + + @Override + protected String getClientName() { + return RpcTypeEnum.MCP.getName(); + } + + @Override + protected void handleClass(final Class<?> clazz, final Object bean, final ShenyuMcpClient beanShenyuClient, final String superPath) { + } + + @Override + protected void handleMethod(final Object bean, final Class<?> clazz, final ShenyuMcpClient beanShenyuClient, final Method method, final String superPath) { + List<String> namespaceIds = this.getNamespace(); + ShenyuMcpClient classMcpClient; + if (clazz.isAnnotationPresent(ShenyuMcpClient.class)) { + classMcpClient = clazz.getAnnotation(ShenyuMcpClient.class); + } else { + return; + } + ShenyuMcpClient methodMcpClient; + if (method.isAnnotationPresent(ShenyuMcpClient.class)) { + methodMcpClient = method.getAnnotation(ShenyuMcpClient.class); + } else { + return; + } + + List<String> mergeUrls = findMergeUrl(clazz, method); + mergeUrls.forEach(url -> { + namespaceIds.forEach(namespaceId -> getPublisher().publishEvent(buildMcpToolsRegisterDTO(bean, clazz, classMcpClient, methodMcpClient, superPath, method, url, namespaceId))); + }); + } + + @Override + protected String buildApiSuperPath(final Class<?> clazz, final ShenyuMcpClient beanShenyuClient) { + Server[] servers = beanShenyuClient.definition().servers(); + if (servers.length != 1) { + log.warn("The shenyuMcp service supports only a single server entry. Please ensure that only one server is configured"); + } + String superUrl = servers[0].url(); + if (StringUtils.isBlank(superUrl)) { + return superUrl; + } + return ""; Review Comment: The logic is inverted. When superUrl is blank, it returns the blank value, but when it's not blank, it returns an empty string. This should return superUrl when it's not blank and empty string when it is blank. ```suggestion return ""; } return superUrl; ``` -- 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]
