Copilot commented on code in PR #7876: URL: https://github.com/apache/incubator-seata/pull/7876#discussion_r2636963475
########## console/src/main/java/org/apache/seata/mcp/core/props/MCPProperties.java: ########## @@ -0,0 +1,107 @@ +/* + * 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.seata.mcp.core.props; + +import jakarta.annotation.PostConstruct; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.TimeUnit; + +@Component +public class MCPProperties { + + public static final String SSE_TYPE = "sse"; + public static final String STREAMABLE_TYPE = "streamable"; + + private boolean enableAuth = true; + + private Long queryDuration = TimeUnit.DAYS.toMillis(1); + + private String mcpType = SSE_TYPE; + + private StreamableProperties streamableProperties; + + private SseServerProperties sseServerProperties; + + public MCPProperties() {} + + public boolean isSseType() { + return mcpType.equals(SSE_TYPE); + } + + public List<String> getEndpoints() { + List<String> result = new ArrayList<>(); + if (isSseType()) { + result.add(sseServerProperties.sseEndpoint); + result.add(sseServerProperties.messageEndpoint); + } else { + result.add(streamableProperties.mcpEndPoint); + } + return result; Review Comment: The getEndpoints method can throw a NullPointerException if called before the init method completes, or if init fails to initialize the properties. When mcpType is SSE_TYPE, sseServerProperties might be null. When mcpType is not SSE_TYPE, streamableProperties might be null. Consider adding null checks or ensuring proper initialization validation. ########## console/src/main/java/org/apache/seata/console/filter/MCPAuthenticationFilter.java: ########## @@ -0,0 +1,81 @@ +/* + * 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.seata.console.filter; + +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.apache.seata.console.security.User; +import org.springframework.http.HttpStatus; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.web.filter.OncePerRequestFilter; + +import java.io.IOException; + +public class MCPAuthenticationFilter extends OncePerRequestFilter { + + private final AuthenticationManager authenticationManager; + + /** + * Instantiates a new Jwt authentication token filter. + * + * @param authenticationManager the token provider + */ + public MCPAuthenticationFilter(AuthenticationManager authenticationManager) { + this.authenticationManager = authenticationManager; + } + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) + throws IOException, ServletException { + + Authentication existingAuth = SecurityContextHolder.getContext().getAuthentication(); + if (existingAuth != null && existingAuth.isAuthenticated()) { + chain.doFilter(request, response); + return; + } + + User user = resolveHeaders(request); + + UsernamePasswordAuthenticationToken authenticationToken = + new UsernamePasswordAuthenticationToken(user.getUsername(), user.getPassword()); + + try { + Authentication authentication = authenticationManager.authenticate(authenticationToken); + SecurityContextHolder.getContext().setAuthentication(authentication); + } catch (BadCredentialsException e) { + response.sendError(HttpStatus.UNAUTHORIZED.value(), "Invalid credentials"); + return; + } + + chain.doFilter(request, response); + } + + /** + * Get userDetails from header + */ + private User resolveHeaders(HttpServletRequest request) { + String username = request.getHeader("X-Mcp-Username"); + String password = request.getHeader("X-Mcp-Password"); + return new User(username, password); + } Review Comment: Sensitive credentials (username and password) are being transmitted in custom HTTP headers. This approach can lead to credentials being logged by proxies, load balancers, or application logs. Consider using standard authentication mechanisms like HTTP Basic Authentication or Bearer tokens, which have better tooling support and are less likely to be inadvertently logged. ########## console/src/main/java/org/apache/seata/console/filter/MCPAuthenticationFilter.java: ########## @@ -0,0 +1,81 @@ +/* + * 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.seata.console.filter; + +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.apache.seata.console.security.User; +import org.springframework.http.HttpStatus; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.web.filter.OncePerRequestFilter; + +import java.io.IOException; + +public class MCPAuthenticationFilter extends OncePerRequestFilter { + + private final AuthenticationManager authenticationManager; + + /** + * Instantiates a new Jwt authentication token filter. + * + * @param authenticationManager the token provider Review Comment: The Javadoc comment incorrectly states "Instantiates a new Jwt authentication token filter" and refers to "the token provider". This should be updated to reflect that this is an MCP authentication filter that uses username/password from headers, not JWT tokens. ```suggestion * Instantiates a new MCP authentication filter that authenticates requests * using username and password provided in HTTP headers. * * @param authenticationManager the authentication manager used to validate credentials ``` ########## console/src/main/java/org/apache/seata/console/config/MCPFiltersConfig.java: ########## @@ -0,0 +1,66 @@ +/* + * 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.seata.console.config; + +import org.apache.seata.console.filter.MCPAuthenticationFilter; +import org.apache.seata.mcp.core.props.MCPProperties; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.Ordered; +import org.springframework.security.authentication.AuthenticationManager; + +@Configuration +public class MCPFiltersConfig { + + @Autowired + private AuthenticationManager authenticationManager; + + @Autowired + private MCPProperties mcpProperties; + + private static final Logger LOGGER = LoggerFactory.getLogger(MCPFiltersConfig.class); + + @Bean + public FilterRegistrationBean<MCPAuthenticationFilter> mcpJwtAuthenticationTokenFilterRegistration() { + + MCPAuthenticationFilter mcpJwtAuthenticationTokenFilter = new MCPAuthenticationFilter(authenticationManager); + + FilterRegistrationBean<MCPAuthenticationFilter> registration = new FilterRegistrationBean<>(); + + registration.setFilter(mcpJwtAuthenticationTokenFilter); + + for (String endPoint : mcpProperties.getEndpoints()) { + registration.addUrlPatterns(endPoint); + } + + registration.setName("mcpJwtAuthenticationTokenFilter"); Review Comment: The method name 'mcpJwtAuthenticationTokenFilterRegistration' and variable 'mcpJwtAuthenticationTokenFilter' reference 'Jwt' but this filter performs basic username/password authentication, not JWT authentication. The naming should reflect the actual authentication mechanism, such as 'mcpAuthenticationFilterRegistration' and 'mcpAuthenticationFilter'. ```suggestion public FilterRegistrationBean<MCPAuthenticationFilter> mcpAuthenticationFilterRegistration() { MCPAuthenticationFilter mcpAuthenticationFilter = new MCPAuthenticationFilter(authenticationManager); FilterRegistrationBean<MCPAuthenticationFilter> registration = new FilterRegistrationBean<>(); registration.setFilter(mcpAuthenticationFilter); for (String endPoint : mcpProperties.getEndpoints()) { registration.addUrlPatterns(endPoint); } registration.setName("mcpAuthenticationFilter"); ``` ########## namingserver/src/main/resources/application.yml: ########## @@ -40,3 +48,10 @@ seata: csrf-ignore-urls: /naming/v1/**,/api/v1/naming/** ignore: urls: /,/**/*.css,/**/*.js,/**/*.html,/**/*.map,/**/*.svg,/**/*.png,/**/*.jpeg,/**/*.ico,/api/v1/auth/login,/version.json,/naming/v1/health,/error + # MCP Server Configuration + mcp: + # Query limits the maximum time interval, The unit is milliseconds, Default one day + query: + max_query_duration: 86400000 Review Comment: The configuration property uses snake_case 'max_query_duration' which is inconsistent with typical Spring Boot property naming conventions that use kebab-case. This should be 'max-query-duration' to follow Spring Boot conventions. ```suggestion max-query-duration: 86400000 ``` ########## console/src/main/java/org/apache/seata/console/filter/MCPAuthenticationFilter.java: ########## @@ -0,0 +1,81 @@ +/* + * 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.seata.console.filter; + +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.apache.seata.console.security.User; +import org.springframework.http.HttpStatus; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.web.filter.OncePerRequestFilter; + +import java.io.IOException; + +public class MCPAuthenticationFilter extends OncePerRequestFilter { + + private final AuthenticationManager authenticationManager; + + /** + * Instantiates a new Jwt authentication token filter. + * + * @param authenticationManager the token provider + */ + public MCPAuthenticationFilter(AuthenticationManager authenticationManager) { + this.authenticationManager = authenticationManager; + } + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) + throws IOException, ServletException { + + Authentication existingAuth = SecurityContextHolder.getContext().getAuthentication(); + if (existingAuth != null && existingAuth.isAuthenticated()) { + chain.doFilter(request, response); + return; + } + + User user = resolveHeaders(request); + + UsernamePasswordAuthenticationToken authenticationToken = + new UsernamePasswordAuthenticationToken(user.getUsername(), user.getPassword()); + + try { + Authentication authentication = authenticationManager.authenticate(authenticationToken); + SecurityContextHolder.getContext().setAuthentication(authentication); + } catch (BadCredentialsException e) { + response.sendError(HttpStatus.UNAUTHORIZED.value(), "Invalid credentials"); + return; + } + + chain.doFilter(request, response); + } + + /** + * Get userDetails from header + */ + private User resolveHeaders(HttpServletRequest request) { + String username = request.getHeader("X-Mcp-Username"); + String password = request.getHeader("X-Mcp-Password"); Review Comment: The resolveHeaders method does not validate that username and password headers are present. If these headers are missing, null values will be passed to the User constructor and subsequently to UsernamePasswordAuthenticationToken. This could lead to unexpected behavior or NullPointerException during authentication. Consider adding validation to return a clear error when required headers are missing. ```suggestion String password = request.getHeader("X-Mcp-Password"); if (username == null || password == null) { throw new BadCredentialsException("Missing authentication headers"); } ``` ########## console/src/main/java/org/apache/seata/mcp/core/props/MCPProperties.java: ########## @@ -0,0 +1,107 @@ +/* + * 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.seata.mcp.core.props; + +import jakarta.annotation.PostConstruct; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.TimeUnit; + +@Component +public class MCPProperties { + + public static final String SSE_TYPE = "sse"; + public static final String STREAMABLE_TYPE = "streamable"; + + private boolean enableAuth = true; + + private Long queryDuration = TimeUnit.DAYS.toMillis(1); + + private String mcpType = SSE_TYPE; + + private StreamableProperties streamableProperties; + + private SseServerProperties sseServerProperties; + + public MCPProperties() {} + + public boolean isSseType() { + return mcpType.equals(SSE_TYPE); + } + + public List<String> getEndpoints() { + List<String> result = new ArrayList<>(); + if (isSseType()) { + result.add(sseServerProperties.sseEndpoint); + result.add(sseServerProperties.messageEndpoint); + } else { + result.add(streamableProperties.mcpEndPoint); + } + return result; + } + + public Long getQueryDuration() { + return queryDuration; + } + + public static class StreamableProperties { + private final String mcpEndPoint; + + public StreamableProperties(String mcpEndPoint) { + this.mcpEndPoint = mcpEndPoint; + } + } + + public static class SseServerProperties { + + private final String sseEndpoint; + + private final String messageEndpoint; + + public SseServerProperties(String sseEndpoint, String messageEndpoint) { + this.sseEndpoint = sseEndpoint; + this.messageEndpoint = messageEndpoint; + } + } + + @Autowired + private Environment env; Review Comment: Field injection is used for the Environment dependency. Constructor injection is preferred over field injection as it makes dependencies explicit, enables immutability, facilitates testing, and prevents NullPointerException risks. Consider using constructor injection instead. ########## console/src/main/java/org/apache/seata/console/filter/MCPAuthenticationFilter.java: ########## @@ -0,0 +1,81 @@ +/* + * 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.seata.console.filter; + +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.apache.seata.console.security.User; +import org.springframework.http.HttpStatus; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.web.filter.OncePerRequestFilter; + +import java.io.IOException; + +public class MCPAuthenticationFilter extends OncePerRequestFilter { + + private final AuthenticationManager authenticationManager; + + /** + * Instantiates a new Jwt authentication token filter. + * + * @param authenticationManager the token provider + */ + public MCPAuthenticationFilter(AuthenticationManager authenticationManager) { + this.authenticationManager = authenticationManager; + } + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) + throws IOException, ServletException { + + Authentication existingAuth = SecurityContextHolder.getContext().getAuthentication(); + if (existingAuth != null && existingAuth.isAuthenticated()) { + chain.doFilter(request, response); + return; + } Review Comment: The authentication check at line 52 uses a weak condition. An authentication object can exist but represent an anonymous user. The check should verify both that authentication exists and that it represents a properly authenticated, non-anonymous user. Consider using `existingAuth.isAuthenticated() && !(existingAuth instanceof AnonymousAuthenticationToken)`. ########## namingserver/src/main/resources/application.yml: ########## @@ -40,3 +48,10 @@ seata: csrf-ignore-urls: /naming/v1/**,/api/v1/naming/** ignore: urls: /,/**/*.css,/**/*.js,/**/*.html,/**/*.map,/**/*.svg,/**/*.png,/**/*.jpeg,/**/*.ico,/api/v1/auth/login,/version.json,/naming/v1/health,/error + # MCP Server Configuration + mcp: + # Query limits the maximum time interval, The unit is milliseconds, Default one day Review Comment: Comment says "Query limits the maximum time interval" but should say "Query limit for the maximum time interval" or "Maximum query time interval". The plural form "limits" is grammatically incorrect here. ```suggestion # Maximum query time interval, The unit is milliseconds, Default one day ``` ########## console/src/main/java/org/apache/seata/console/filter/MCPAuthenticationFilter.java: ########## @@ -0,0 +1,81 @@ +/* + * 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.seata.console.filter; + +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.apache.seata.console.security.User; +import org.springframework.http.HttpStatus; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.web.filter.OncePerRequestFilter; + +import java.io.IOException; + +public class MCPAuthenticationFilter extends OncePerRequestFilter { + + private final AuthenticationManager authenticationManager; + + /** + * Instantiates a new Jwt authentication token filter. + * + * @param authenticationManager the token provider + */ + public MCPAuthenticationFilter(AuthenticationManager authenticationManager) { + this.authenticationManager = authenticationManager; + } + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) + throws IOException, ServletException { + + Authentication existingAuth = SecurityContextHolder.getContext().getAuthentication(); + if (existingAuth != null && existingAuth.isAuthenticated()) { + chain.doFilter(request, response); + return; + } + + User user = resolveHeaders(request); + + UsernamePasswordAuthenticationToken authenticationToken = + new UsernamePasswordAuthenticationToken(user.getUsername(), user.getPassword()); + + try { + Authentication authentication = authenticationManager.authenticate(authenticationToken); + SecurityContextHolder.getContext().setAuthentication(authentication); + } catch (BadCredentialsException e) { + response.sendError(HttpStatus.UNAUTHORIZED.value(), "Invalid credentials"); + return; + } Review Comment: The code only catches BadCredentialsException but the authenticationManager.authenticate() can throw other AuthenticationException subtypes such as DisabledException, LockedException, or AccountExpiredException. These exceptions would propagate uncaught, potentially exposing stack traces. Consider catching the broader AuthenticationException to handle all authentication failures gracefully. ########## console/src/main/java/org/apache/seata/mcp/core/props/MCPProperties.java: ########## @@ -0,0 +1,107 @@ +/* + * 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.seata.mcp.core.props; + +import jakarta.annotation.PostConstruct; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.TimeUnit; + +@Component +public class MCPProperties { + + public static final String SSE_TYPE = "sse"; + public static final String STREAMABLE_TYPE = "streamable"; + + private boolean enableAuth = true; + + private Long queryDuration = TimeUnit.DAYS.toMillis(1); + + private String mcpType = SSE_TYPE; + + private StreamableProperties streamableProperties; + + private SseServerProperties sseServerProperties; + + public MCPProperties() {} + + public boolean isSseType() { + return mcpType.equals(SSE_TYPE); + } + + public List<String> getEndpoints() { + List<String> result = new ArrayList<>(); + if (isSseType()) { + result.add(sseServerProperties.sseEndpoint); + result.add(sseServerProperties.messageEndpoint); + } else { + result.add(streamableProperties.mcpEndPoint); + } + return result; + } + + public Long getQueryDuration() { + return queryDuration; + } + + public static class StreamableProperties { + private final String mcpEndPoint; + + public StreamableProperties(String mcpEndPoint) { + this.mcpEndPoint = mcpEndPoint; + } + } + + public static class SseServerProperties { + + private final String sseEndpoint; + + private final String messageEndpoint; + + public SseServerProperties(String sseEndpoint, String messageEndpoint) { + this.sseEndpoint = sseEndpoint; + this.messageEndpoint = messageEndpoint; + } + } + + @Autowired + private Environment env; + + @PostConstruct + public void init() { + mcpType = env.getProperty("spring.ai.mcp.server.protocol", "sse"); + if (mcpType.equals(STREAMABLE_TYPE)) { + String mcpEndPoint = env.getProperty("spring.ai.mcp.server.streamable-http.mcp-endpoint", "/mcp"); + streamableProperties = new StreamableProperties(mcpEndPoint); + } else { + mcpType = SSE_TYPE; + String sseEndpoint = env.getProperty("spring.ai.mcp.server.sse-endpoint", "/sse"); + String messageEndpoint = env.getProperty("spring.ai.mcp.server.sse-message-endpoint", "/mcp/message"); + sseServerProperties = new SseServerProperties(sseEndpoint, messageEndpoint); + } + queryDuration = Long.parseLong(env.getProperty("seata.mcp.query.max_query_duration", "86400000")); Review Comment: Potential uncaught 'java.lang.NumberFormatException'. ```suggestion String maxQueryDurationStr = env.getProperty("seata.mcp.query.max_query_duration", "86400000"); try { queryDuration = Long.parseLong(maxQueryDurationStr); } catch (NumberFormatException ex) { queryDuration = TimeUnit.DAYS.toMillis(1); } ``` ########## console/src/main/java/org/apache/seata/mcp/core/props/MCPProperties.java: ########## @@ -0,0 +1,107 @@ +/* + * 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.seata.mcp.core.props; + +import jakarta.annotation.PostConstruct; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.TimeUnit; + +@Component +public class MCPProperties { + + public static final String SSE_TYPE = "sse"; + public static final String STREAMABLE_TYPE = "streamable"; + + private boolean enableAuth = true; + + private Long queryDuration = TimeUnit.DAYS.toMillis(1); + + private String mcpType = SSE_TYPE; + + private StreamableProperties streamableProperties; + + private SseServerProperties sseServerProperties; + + public MCPProperties() {} + + public boolean isSseType() { + return mcpType.equals(SSE_TYPE); + } + + public List<String> getEndpoints() { + List<String> result = new ArrayList<>(); + if (isSseType()) { + result.add(sseServerProperties.sseEndpoint); + result.add(sseServerProperties.messageEndpoint); + } else { + result.add(streamableProperties.mcpEndPoint); + } + return result; + } + + public Long getQueryDuration() { + return queryDuration; + } + + public static class StreamableProperties { + private final String mcpEndPoint; Review Comment: The naming convention 'mcpEndPoint' uses inconsistent capitalization - 'Point' should be 'point' (camelCase). For consistency with Java naming conventions, this should be 'mcpEndpoint'. -- 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]
