Skip to content

Commit ff3635b

Browse files
Thomas-EliotJayLi52HecarimVwxm155hongzhouzi
authored
Feature/issue 514 mcp server manage (higress-group#530)
Co-authored-by: LiYongjie <1169268551@qq.com> Co-authored-by: LiYongjie <lyj02179318@alibaba-inc.com> Co-authored-by: HecarimV <lhj02226479@alibaba-inc.com> Co-authored-by: 孚阳 <fuyang.wxm@alibaba-inc.com> Co-authored-by: hongzhouzi <weihongzhou.whz@alibaba-inc.com> Co-authored-by: lz19931106 <lc02152203@alibaba-inc.com>
1 parent 83cdd86 commit ff3635b

File tree

111 files changed

+8363
-481
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

111 files changed

+8363
-481
lines changed

.licenserc.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ header:
1919
- 'backend/mvnw*'
2020
- 'backend/**/pom.xml'
2121
- 'backend/*.sh'
22+
- 'backend/tools/**'
2223
- 'backend/**/*.json'
2324
- 'backend/**/*.html'
2425
- 'backend/**/*.md'

backend/.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
**/src/main/resources/static/
2-
**/src/main/resources/git.properties
2+
**/src/main/resources/git.properties
3+
/build-image.sh

backend/Dockerfile

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,11 @@ WORKDIR /app
88
COPY console/target/higress-console.jar /app
99
COPY start.sh /app
1010

11+
ARG TARGETARCH
12+
COPY tools/mcp/${TARGETARCH}/main /app/tools/mcp/
13+
COPY tools/mcp/openapiToMcpserver.sh /app/tools/mcp/
14+
RUN chmod +x /app/tools/mcp/openapiToMcpserver.sh \
15+
&& chmod +x /app/tools/mcp/main
16+
1117
EXPOSE 8080
1218
CMD ["/app/start.sh"]

backend/console/src/main/java/com/alibaba/higress/console/config/SdkConfig.java

Lines changed: 36 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,14 @@
1212
*/
1313
package com.alibaba.higress.console.config;
1414

15+
import java.io.IOException;
16+
17+
import javax.annotation.PostConstruct;
18+
19+
import org.springframework.beans.factory.annotation.Value;
20+
import org.springframework.context.annotation.Bean;
21+
import org.springframework.context.annotation.Configuration;
22+
1523
import com.alibaba.higress.console.constant.SystemConfigKey;
1624
import com.alibaba.higress.sdk.config.HigressServiceConfig;
1725
import com.alibaba.higress.sdk.constant.HigressConstants;
@@ -29,21 +37,17 @@
2937
import com.alibaba.higress.sdk.service.consumer.ConsumerService;
3038
import com.alibaba.higress.sdk.service.kubernetes.KubernetesClientService;
3139
import com.alibaba.higress.sdk.service.kubernetes.KubernetesModelConverter;
32-
import org.springframework.beans.factory.annotation.Value;
33-
import org.springframework.context.annotation.Bean;
34-
import org.springframework.context.annotation.Configuration;
35-
36-
import javax.annotation.PostConstruct;
37-
import java.io.IOException;
40+
import com.alibaba.higress.sdk.service.mcp.McpServerHelper;
41+
import com.alibaba.higress.sdk.service.mcp.McpServerService;
3842

3943
@Configuration
4044
public class SdkConfig {
4145

4246
@Value("${" + SystemConfigKey.KUBE_CONFIG_KEY + ":}")
4347
private String kubeConfig;
4448

45-
@Value(
46-
"${" + SystemConfigKey.CONTROLLER_SERVICE_NAME_KEY + ":" + HigressConstants.CONTROLLER_SERVICE_NAME_DEFAULT + "}")
49+
@Value("${" + SystemConfigKey.CONTROLLER_SERVICE_NAME_KEY + ":" + HigressConstants.CONTROLLER_SERVICE_NAME_DEFAULT
50+
+ "}")
4751
private String controllerServiceName = HigressConstants.CONTROLLER_SERVICE_NAME_DEFAULT;
4852

4953
@Value("${" + SystemConfigKey.NS_KEY + ":" + HigressConstants.NS_DEFAULT + "}")
@@ -55,16 +59,16 @@ public class SdkConfig {
5559
@Value("${" + SystemConfigKey.CONTROLLER_INGRESS_CLASS_NAME_KEY + ":}")
5660
private String controllerWatchedIngressClassName;
5761

58-
@Value(
59-
"${" + SystemConfigKey.CONTROLLER_SERVICE_HOST_KEY + ":" + HigressConstants.CONTROLLER_SERVICE_HOST_DEFAULT + "}")
62+
@Value("${" + SystemConfigKey.CONTROLLER_SERVICE_HOST_KEY + ":" + HigressConstants.CONTROLLER_SERVICE_HOST_DEFAULT
63+
+ "}")
6064
private String controllerServiceHost = HigressConstants.CONTROLLER_SERVICE_HOST_DEFAULT;
6165

62-
@Value(
63-
"${" + SystemConfigKey.CONTROLLER_SERVICE_PORT_KEY + ":" + HigressConstants.CONTROLLER_SERVICE_PORT_DEFAULT + "}")
66+
@Value("${" + SystemConfigKey.CONTROLLER_SERVICE_PORT_KEY + ":" + HigressConstants.CONTROLLER_SERVICE_PORT_DEFAULT
67+
+ "}")
6468
private int controllerServicePort = HigressConstants.CONTROLLER_SERVICE_PORT_DEFAULT;
6569

66-
@Value(
67-
"${" + SystemConfigKey.CONTROLLER_JWT_POLICY_KEY + ":" + HigressConstants.CONTROLLER_JWT_POLICY_DEFAULT + "}")
70+
@Value("${" + SystemConfigKey.CONTROLLER_JWT_POLICY_KEY + ":" + HigressConstants.CONTROLLER_JWT_POLICY_DEFAULT
71+
+ "}")
6872
private String controllerJwtPolicy = HigressConstants.CONTROLLER_JWT_POLICY_DEFAULT;
6973

7074
@Value("${" + SystemConfigKey.CONTROLLER_ACCESS_TOKEN_KEY + ":}")
@@ -77,15 +81,13 @@ public class SdkConfig {
7781

7882
@PostConstruct
7983
public void initialize() throws IOException {
80-
HigressServiceConfig config =
81-
HigressServiceConfig.builder().withKubeConfigPath(kubeConfig).withControllerNamespace(controllerNamespace)
82-
.withControllerWatchedNamespace(controllerWatchedNamespace)
83-
.withControllerWatchedIngressClassName(controllerWatchedIngressClassName)
84-
.withControllerServiceName(controllerServiceName).withControllerServiceHost(controllerServiceHost)
85-
.withControllerServicePort(controllerServicePort).withControllerJwtPolicy(controllerJwtPolicy)
86-
.withControllerAccessToken(controllerAccessToken)
87-
.withClusterDomainSuffix(clusterDomainSuffix)
88-
.withWasmPluginServiceConfig(WasmPluginServiceConfig.buildFromEnv()).build();
84+
HigressServiceConfig config = HigressServiceConfig.builder().withKubeConfigPath(kubeConfig)
85+
.withControllerNamespace(controllerNamespace).withControllerWatchedNamespace(controllerWatchedNamespace)
86+
.withControllerWatchedIngressClassName(controllerWatchedIngressClassName)
87+
.withControllerServiceName(controllerServiceName).withControllerServiceHost(controllerServiceHost)
88+
.withControllerServicePort(controllerServicePort).withControllerJwtPolicy(controllerJwtPolicy)
89+
.withControllerAccessToken(controllerAccessToken).withClusterDomainSuffix(clusterDomainSuffix)
90+
.withWasmPluginServiceConfig(WasmPluginServiceConfig.buildFromEnv()).build();
8991
serviceProvider = HigressServiceProvider.create(config);
9092
}
9193

@@ -148,4 +150,15 @@ public AiRouteService aiRouteService() {
148150
public LlmProviderService llmProviderService() {
149151
return serviceProvider.llmProviderService();
150152
}
153+
154+
@Bean
155+
public McpServerService mcpServerService() {
156+
return serviceProvider.mcpServerService();
157+
}
158+
159+
@Bean
160+
public McpServerHelper mcpServerHelper() {
161+
return new McpServerHelper();
162+
}
163+
151164
}

backend/console/src/main/java/com/alibaba/higress/console/config/SwaggerConfig.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626
import com.alibaba.higress.sdk.model.ai.LlmProviderType;
2727
import com.alibaba.higress.sdk.model.consumer.CredentialType;
2828
import com.alibaba.higress.sdk.model.consumer.KeyAuthCredentialSource;
29+
import com.alibaba.higress.sdk.model.mcp.McpServerDBTypeEnum;
30+
import com.alibaba.higress.sdk.model.mcp.McpServerTypeEnum;
2931
import com.alibaba.higress.sdk.model.route.RoutePredicateTypeEnum;
3032

3133
import io.swagger.v3.core.converter.AnnotatedType;
@@ -58,6 +60,8 @@ private void openApiCustomizer(OpenAPI openApi) {
5860

5961
registerClassSchema(openApi, CapabilityKey.class);
6062
registerClassSchema(openApi, RoutePredicateTypeEnum.class);
63+
registerClassSchema(openApi, McpServerTypeEnum.class);
64+
registerClassSchema(openApi, McpServerDBTypeEnum.class);
6165
registerClassSchema(openApi, LlmProviderType.class);
6266
registerClassSchema(openApi, LlmProviderProtocol.class);
6367
registerClassSchema(openApi, AiRouteFallbackStrategy.class);
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
/*
2+
* Copyright (c) 2022-2023 Alibaba Group Holding Ltd.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
5+
* the License. You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
10+
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
11+
* specific language governing permissions and limitations under the License.
12+
*/
13+
package com.alibaba.higress.console.controller.mcp;
14+
15+
import javax.annotation.Resource;
16+
import javax.validation.Valid;
17+
import javax.validation.constraints.NotBlank;
18+
19+
import org.springdoc.api.annotations.ParameterObject;
20+
import org.springframework.http.ResponseEntity;
21+
import org.springframework.validation.annotation.Validated;
22+
import org.springframework.web.bind.annotation.DeleteMapping;
23+
import org.springframework.web.bind.annotation.GetMapping;
24+
import org.springframework.web.bind.annotation.PathVariable;
25+
import org.springframework.web.bind.annotation.PostMapping;
26+
import org.springframework.web.bind.annotation.PutMapping;
27+
import org.springframework.web.bind.annotation.RequestBody;
28+
import org.springframework.web.bind.annotation.RequestMapping;
29+
import org.springframework.web.bind.annotation.RestController;
30+
31+
import com.alibaba.higress.console.controller.dto.PaginatedResponse;
32+
import com.alibaba.higress.console.controller.dto.Response;
33+
import com.alibaba.higress.console.controller.util.ControllerUtil;
34+
import com.alibaba.higress.sdk.model.mcp.McpServer;
35+
import com.alibaba.higress.sdk.model.mcp.McpServerConsumerDetail;
36+
import com.alibaba.higress.sdk.model.mcp.McpServerConsumers;
37+
import com.alibaba.higress.sdk.model.mcp.McpServerConsumersPageQuery;
38+
import com.alibaba.higress.sdk.model.mcp.McpServerPageQuery;
39+
import com.alibaba.higress.sdk.model.mcp.SwaggerContent;
40+
import com.alibaba.higress.sdk.service.mcp.McpServerHelper;
41+
import com.alibaba.higress.sdk.service.mcp.McpServerService;
42+
43+
import io.swagger.v3.oas.annotations.Operation;
44+
import io.swagger.v3.oas.annotations.responses.ApiResponse;
45+
import io.swagger.v3.oas.annotations.responses.ApiResponses;
46+
import io.swagger.v3.oas.annotations.tags.Tag;
47+
48+
/**
49+
* @author HecarimV
50+
*/
51+
@RestController("McpServerController")
52+
@RequestMapping("/v1/mcpServer")
53+
@Validated
54+
@Tag(name = "Mcp APIs")
55+
public class McpServerController {
56+
57+
@Resource
58+
private McpServerService mcpServerService;
59+
60+
@Resource
61+
private McpServerHelper mcpServerHelper;
62+
63+
@PostMapping("/swaggerToMcpConfig")
64+
@Operation(summary = "swagger to mcp config")
65+
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "swagger convert successfully"),
66+
@ApiResponse(responseCode = "500", description = "Internal server error")})
67+
public ResponseEntity<Response<String>> swaggerToMcpConfig(@Valid @RequestBody SwaggerContent swaggerContent) {
68+
return ResponseEntity.ok(Response.success(mcpServerHelper.swaggerToMcpConfig(swaggerContent.getContent())));
69+
}
70+
71+
@PutMapping
72+
@Operation(summary = "Add or update a mcp server instance")
73+
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Instances saved successfully"),
74+
@ApiResponse(responseCode = "500", description = "Internal server error")})
75+
public ResponseEntity<Response<McpServer>> addOrUpdateMcpInstance(@RequestBody McpServer instance) {
76+
instance = mcpServerService.addOrUpdateWithAuthorization(instance);
77+
return ControllerUtil.buildResponseEntity(instance);
78+
}
79+
80+
@GetMapping
81+
@Operation(summary = "List mcp server")
82+
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "McpServers listed successfully"),
83+
@ApiResponse(responseCode = "500", description = "Internal server error")})
84+
public ResponseEntity<PaginatedResponse<McpServer>> list(@ParameterObject McpServerPageQuery query) {
85+
return ControllerUtil.buildResponseEntity(mcpServerService.list(query));
86+
}
87+
88+
@GetMapping("/{name}")
89+
@Operation(summary = "Get detail for mcp server")
90+
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "McpServer detail query successfully"),
91+
@ApiResponse(responseCode = "500", description = "Internal server error")})
92+
public ResponseEntity<Response<McpServer>> query(@PathVariable("name") @NotBlank String name) {
93+
return ControllerUtil.buildResponseEntity(mcpServerService.query(name));
94+
}
95+
96+
@DeleteMapping("/{name}")
97+
@Operation(summary = "Delete a mcp server")
98+
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "Route deleted successfully"),
99+
@ApiResponse(responseCode = "500", description = "Internal server error")})
100+
public ResponseEntity<Void> delete(@PathVariable("name") @NotBlank String name) {
101+
mcpServerService.delete(name);
102+
return ResponseEntity.noContent().build();
103+
}
104+
105+
@PutMapping("/consumers")
106+
@Operation(summary = "Add mcp server allow consumers")
107+
@ApiResponses(
108+
value = {@ApiResponse(responseCode = "204", description = "Add mcp server allow consumers successfully"),
109+
@ApiResponse(responseCode = "500", description = "Internal server error")})
110+
public ResponseEntity<Void> addAllowConsumers(@RequestBody McpServerConsumers consumers) {
111+
mcpServerService.addAllowConsumers(consumers);
112+
return ResponseEntity.noContent().build();
113+
}
114+
115+
@DeleteMapping("/consumers")
116+
@Operation(summary = "Delete mcp server allow consumers")
117+
@ApiResponses(
118+
value = {@ApiResponse(responseCode = "204", description = "Delete mcp server allow consumers successfully"),
119+
@ApiResponse(responseCode = "500", description = "Internal server error")})
120+
public ResponseEntity<Void> deleteAllowConsumers(@RequestBody McpServerConsumers consumers) {
121+
mcpServerService.deleteAllowConsumers(consumers);
122+
return ResponseEntity.noContent().build();
123+
}
124+
125+
@GetMapping("/consumers")
126+
@Operation(summary = "List mcp server allow consumers")
127+
@ApiResponses(
128+
value = {@ApiResponse(responseCode = "200", description = "List mcp server allow consumers successfully"),
129+
@ApiResponse(responseCode = "500", description = "Internal server error")})
130+
public ResponseEntity<PaginatedResponse<McpServerConsumerDetail>>
131+
listAllowConsumers(@ParameterObject McpServerConsumersPageQuery query) {
132+
return ControllerUtil.buildResponseEntity(mcpServerService.listAllowConsumers(query));
133+
}
134+
135+
}

backend/console/src/main/resources/application.properties

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
# an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
1111
# specific language governing permissions and limitations under the License.
1212
#
13-
1413
springdoc.api-docs.enabled=false
1514
springdoc.pathsToMatch=/v1/**,/session/**,/dashboard/**,/system/**,/user/**
1615
springdoc.swagger-ui.enabled=false
@@ -26,4 +25,4 @@ server.compression.min-response-size=2048
2625
server.compression.excluded-user-agents=MSIE 6.0,UCBrowser
2726

2827
# Add %mdc (Mapped Diagnostic Context) based on the Spring Boot default logging pattern found in org.springframework.boot.logging.logback.DefaultLogbackConfiguration
29-
logging.pattern.console=%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(%5p) %clr(70396){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} {%mdc} %clr(:){faint} %m%n%wEx
28+
logging.pattern.console=%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(%5p) %clr(70396){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} {%mdc} %clr(:){faint} %m%n%wEx

backend/sdk/src/main/java/com/alibaba/higress/sdk/config/HigressServiceConfig.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
public class HigressServiceConfig {
3030

3131
private final String kubeConfigPath;
32+
private final String kubeConfigContent;
3233
private final String controllerNamespace;
3334
private final String controllerWatchedNamespace;
3435
private final String controllerWatchedIngressClassName;
@@ -73,6 +74,7 @@ public static HigressServiceConfig.Builder builder() {
7374

7475
public static final class Builder {
7576
private String kubeConfigPath;
77+
private String kubeConfigContent;
7678
private String controllerWatchedNamespace;
7779
private String controllerWatchedIngressClassName = HigressConstants.CONTROLLER_INGRESS_CLASS_NAME_DEFAULT;
7880
private String controllerNamespace = HigressConstants.NS_DEFAULT;
@@ -102,6 +104,11 @@ public Builder withClusterDomainSuffix(String clusterDomainSuffix) {
102104
return this;
103105
}
104106

107+
public Builder withKubeConfigContent(String kubeConfigContent) {
108+
this.kubeConfigContent = kubeConfigContent;
109+
return this;
110+
}
111+
105112
public Builder withKubeConfigPath(String kubeConfigPath) {
106113
this.kubeConfigPath = kubeConfigPath;
107114
return this;
@@ -156,7 +163,7 @@ public Builder withControllerAccessToken(String controllerAccessToken) {
156163
}
157164

158165
public HigressServiceConfig build() {
159-
return new HigressServiceConfig(kubeConfigPath,
166+
return new HigressServiceConfig(kubeConfigPath, kubeConfigContent,
160167
StringUtils.firstNonEmpty(controllerNamespace, HigressConstants.NS_DEFAULT), controllerWatchedNamespace,
161168
controllerWatchedIngressClassName,
162169
StringUtils.firstNonEmpty(controllerServiceName, HigressConstants.CONTROLLER_SERVICE_NAME_DEFAULT),

backend/sdk/src/main/java/com/alibaba/higress/sdk/constant/CommonKey.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,6 @@ public class CommonKey {
3131
public static final String AI_ROUTE_PREFIX = AI_ROUTE + Separators.DASH;
3232

3333
public static final String LLM_SERVICE_NAME_PREFIX = "llm-";
34+
35+
public static final String MCP_SERVER_ROUTE_PREFIX = "mcp-server" + Separators.DASH;
3436
}

backend/sdk/src/main/java/com/alibaba/higress/sdk/constant/KubernetesConstants.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ public static class Label {
8686
public static final String WASM_PLUGIN_VERSION_KEY = "higress.io/wasm-plugin-version";
8787
public static final String WASM_PLUGIN_BUILT_IN_KEY = "higress.io/wasm-plugin-built-in";
8888
public static final String WASM_PLUGIN_CATEGORY_KEY = "higress.io/wasm-plugin-category";
89+
public static final String RESOURCE_BIZ_TYPE_KEY = "higress.io/biz-type";
8990
}
9091

9192
public static class IngressPathType {

0 commit comments

Comments
 (0)