diff --git a/.licenserc.yaml b/.licenserc.yaml
index f298fffad..d880a9c78 100644
--- a/.licenserc.yaml
+++ b/.licenserc.yaml
@@ -41,6 +41,7 @@ header:
- 'pnpm-lock.yaml'
- '**/*.txt'
- '**/*.md'
+ - '**/*.prompt'
comment: on-failure
@@ -109,6 +110,20 @@ dependency:
license: EDL-1.0
- name: org.jline:jline
license: BSD-3-Clause
+ - name: com.github.victools:jsonschema-module-jackson
+ license: Apache-2.0
+ - name: com.fasterxml.jackson.module:jackson-module-jsonSchema
+ license: Apache-2.0
+ - name: io.swagger.core.v3:swagger-annotations
+ license: Apache-2.0
+ - name: com.github.victools:jsonschema-module-swagger-2
+ license: Apache-2.0
+ - name: org.antlr:antlr-runtime
+ license: BSD-3-Clause
+ - name: org.antlr:antlr4-runtime
+ license: BSD-3-Clause
+ - name: org.antlr:ST4
+ license: BSD-3-Clause
# Weak compatible licenses
- name: javax.servlet.jsp:jsp-api
license: CDDL-1.1
diff --git a/bigtop-manager-bom/pom.xml b/bigtop-manager-bom/pom.xml
index 3bc38e605..4b078b9f0 100644
--- a/bigtop-manager-bom/pom.xml
+++ b/bigtop-manager-bom/pom.xml
@@ -31,6 +31,7 @@
Bigtop Manager Bom
+ 1.0.0-RC1
3.1.1
2.2.0
2.3.32
@@ -55,6 +56,7 @@
1.0.1-beta6
3.0.3
2.1.0
+ 4.29.0
@@ -276,11 +278,25 @@
langchain4j-community-qianfan
${langchain4j.version}
+
+
+ org.springframework.ai
+ spring-ai-starter-mcp-server-webmvc
+ ${spring-ai.version}
+
+
+
+ com.github.victools
+ jsonschema-module-jackson
+ ${victools.version}
+
+
dev.langchain4j
langchain4j-community-dashscope
${langchain4j.version}
+
dev.langchain4j
langchain4j-reactor
diff --git a/bigtop-manager-common/src/main/java/org/apache/bigtop/manager/common/utils/ProjectPathUtils.java b/bigtop-manager-common/src/main/java/org/apache/bigtop/manager/common/utils/ProjectPathUtils.java
index f8f63c08e..1cd620f3a 100644
--- a/bigtop-manager-common/src/main/java/org/apache/bigtop/manager/common/utils/ProjectPathUtils.java
+++ b/bigtop-manager-common/src/main/java/org/apache/bigtop/manager/common/utils/ProjectPathUtils.java
@@ -51,6 +51,10 @@ public static String getAgentCachePath() {
return getProjectStoreDir() + File.separator + "agent-caches";
}
+ public static String getPromptsPath() {
+ return getProjectResourcesDir() + File.separator + "prompts";
+ }
+
private static String getProjectResourcesDir() {
if (Environments.isDevMode()) {
return Objects.requireNonNull(ProjectPathUtils.class.getResource("/"))
diff --git a/bigtop-manager-server/pom.xml b/bigtop-manager-server/pom.xml
index 539592057..7d4545749 100644
--- a/bigtop-manager-server/pom.xml
+++ b/bigtop-manager-server/pom.xml
@@ -63,7 +63,10 @@
org.apache.bigtop
bigtop-manager-ai
-
+
+ com.github.victools
+ jsonschema-module-jackson
+
org.springframework.boot
spring-boot-starter-web
@@ -74,6 +77,11 @@
spring-boot-starter-webflux
+
+ org.springframework.ai
+ spring-ai-starter-mcp-server-webmvc
+
+
org.springframework.boot
spring-boot-starter-validation
diff --git a/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/config/McpConfig.java b/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/config/McpConfig.java
new file mode 100644
index 000000000..700bd9430
--- /dev/null
+++ b/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/config/McpConfig.java
@@ -0,0 +1,39 @@
+/*
+ * 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
+ *
+ * https://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.bigtop.manager.server.config;
+
+import org.apache.bigtop.manager.server.holder.SpringContextHolder;
+import org.apache.bigtop.manager.server.mcp.tool.McpTool;
+
+import org.springframework.ai.tool.ToolCallbackProvider;
+import org.springframework.ai.tool.method.MethodToolCallbackProvider;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class McpConfig {
+
+ @Bean
+ public ToolCallbackProvider mcpTools() {
+ return MethodToolCallbackProvider.builder()
+ .toolObjects(
+ (Object[]) SpringContextHolder.getMcpTools().values().toArray(new McpTool[0]))
+ .build();
+ }
+}
diff --git a/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/config/WebConfig.java b/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/config/WebConfig.java
index 15ff1ce6d..efb46e690 100644
--- a/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/config/WebConfig.java
+++ b/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/config/WebConfig.java
@@ -19,6 +19,7 @@
package org.apache.bigtop.manager.server.config;
import org.apache.bigtop.manager.server.interceptor.AuthInterceptor;
+import org.apache.bigtop.manager.server.interceptor.MCPInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
@@ -35,6 +36,9 @@ public class WebConfig implements WebMvcConfigurer {
@Resource
private AuthInterceptor authInterceptor;
+ @Resource
+ private MCPInterceptor mcpInterceptor;
+
private static final String API_PREFIX = "/api";
private static final String PREFIXED_PACKAGE = "org.apache.bigtop.manager.server.controller";
@@ -45,10 +49,14 @@ public void addInterceptors(InterceptorRegistry registry) {
.addPathPatterns("/**")
// Server APIs
.excludePathPatterns("/api/salt", "/api/nonce", "/api/login")
+ // MCP APIs
+ .excludePathPatterns("/mcp/**")
// Frontend pages
.excludePathPatterns("/", "/ui/**", "/favicon.ico", "/error")
// Swagger pages
.excludePathPatterns("/swagger-ui/**", "/v3/**", "/swagger-ui.html");
+
+ registry.addInterceptor(mcpInterceptor).addPathPatterns("/mcp/**");
}
@Override
diff --git a/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/holder/SpringContextHolder.java b/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/holder/SpringContextHolder.java
index 381ba6832..7f38c1763 100644
--- a/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/holder/SpringContextHolder.java
+++ b/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/holder/SpringContextHolder.java
@@ -20,6 +20,7 @@
import org.apache.bigtop.manager.server.command.factory.JobFactory;
import org.apache.bigtop.manager.server.command.validator.CommandValidator;
+import org.apache.bigtop.manager.server.mcp.tool.McpTool;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
@@ -52,4 +53,8 @@ public static Map getCommandValidators() {
public static Map getJobFactories() {
return applicationContext.getBeansOfType(JobFactory.class);
}
+
+ public static Map getMcpTools() {
+ return applicationContext.getBeansOfType(McpTool.class);
+ }
}
diff --git a/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/interceptor/MCPInterceptor.java b/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/interceptor/MCPInterceptor.java
new file mode 100644
index 000000000..f02bcab4c
--- /dev/null
+++ b/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/interceptor/MCPInterceptor.java
@@ -0,0 +1,68 @@
+/*
+ * 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
+ *
+ * https://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.bigtop.manager.server.interceptor;
+
+import org.apache.bigtop.manager.common.utils.JsonUtils;
+import org.apache.bigtop.manager.server.utils.ResponseEntity;
+
+import org.springframework.stereotype.Component;
+import org.springframework.web.servlet.HandlerInterceptor;
+import org.springframework.web.servlet.ModelAndView;
+
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+
+@Component
+public class MCPInterceptor implements HandlerInterceptor {
+
+ private ResponseEntity> responseEntity;
+
+ @Override
+ public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
+ throws Exception {
+ if (checkAuthenticated(request)) {
+ return HandlerInterceptor.super.preHandle(request, response, handler);
+ } else {
+ response.setHeader("Content-Type", "application/json; charset=UTF-8");
+ response.getWriter().write(JsonUtils.writeAsString(responseEntity));
+ return false;
+ }
+ }
+
+ @Override
+ public void postHandle(
+ HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
+ throws Exception {
+ HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
+ }
+
+ @Override
+ public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
+ throws Exception {
+ HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
+ }
+
+ private Boolean checkAuthenticated(HttpServletRequest request) {
+ return true;
+ }
+
+ private Boolean checkPermission() {
+ return true;
+ }
+}
diff --git a/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/mcp/converter/JsonToolCallResultConverter.java b/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/mcp/converter/JsonToolCallResultConverter.java
new file mode 100644
index 000000000..3632001e5
--- /dev/null
+++ b/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/mcp/converter/JsonToolCallResultConverter.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
+ *
+ * https://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.bigtop.manager.server.mcp.converter;
+
+import org.apache.bigtop.manager.common.utils.JsonUtils;
+
+import org.jetbrains.annotations.NotNull;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.ai.tool.execution.ToolCallResultConverter;
+import org.springframework.lang.Nullable;
+
+import javax.imageio.ImageIO;
+import java.awt.image.RenderedImage;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.lang.reflect.Type;
+import java.util.Base64;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * Custom converter only replace JsonParser to JsonUtils.
+ * See original source code from {@link org.springframework.ai.tool.execution.DefaultToolCallResultConverter}
+ */
+public class JsonToolCallResultConverter implements ToolCallResultConverter {
+
+ private static final Logger logger = LoggerFactory.getLogger(JsonToolCallResultConverter.class);
+
+ @NotNull @Override
+ public String convert(@Nullable Object result, @Nullable Type returnType) {
+ if (returnType == Void.TYPE) {
+ logger.debug("The tool has no return type. Converting to conventional response.");
+ return JsonUtils.writeAsString("Done");
+ }
+ if (result instanceof RenderedImage) {
+ final var buf = new ByteArrayOutputStream(1024 * 4);
+ try {
+ ImageIO.write((RenderedImage) result, "PNG", buf);
+ } catch (IOException e) {
+ return "Failed to convert tool result to a base64 image: " + e.getMessage();
+ }
+ final var imgB64 = Base64.getEncoder().encodeToString(buf.toByteArray());
+ return JsonUtils.writeAsString(Map.of("mimeType", "image/png", "data", imgB64));
+ } else {
+ logger.debug("Converting tool result to JSON.");
+ return Objects.requireNonNull(JsonUtils.writeAsString(result));
+ }
+ }
+}
diff --git a/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/mcp/prompt/Prompts.java b/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/mcp/prompt/Prompts.java
new file mode 100644
index 000000000..9ee0677ef
--- /dev/null
+++ b/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/mcp/prompt/Prompts.java
@@ -0,0 +1,44 @@
+/*
+ * 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
+ *
+ * https://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.bigtop.manager.server.mcp.prompt;
+
+import org.apache.bigtop.manager.common.utils.FileUtils;
+import org.apache.bigtop.manager.common.utils.ProjectPathUtils;
+import org.apache.bigtop.manager.server.exception.ServerException;
+
+import java.io.File;
+
+/**
+ * Static prompts for tools.
+ */
+public class Prompts {
+
+ public static final String SAMPLE = getText("sample.prompt");
+
+ private static String getText(String filename) {
+ String promptPath = ProjectPathUtils.getPromptsPath();
+ String filePath = promptPath + File.separator + filename;
+
+ try {
+ return FileUtils.readFile2Str(new File(filePath));
+ } catch (Exception e) {
+ throw new ServerException("Error reading prompt file: " + filePath, e);
+ }
+ }
+}
diff --git a/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/mcp/tool/McpTool.java b/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/mcp/tool/McpTool.java
new file mode 100644
index 000000000..0968c5410
--- /dev/null
+++ b/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/mcp/tool/McpTool.java
@@ -0,0 +1,21 @@
+/*
+ * 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
+ *
+ * https://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.bigtop.manager.server.mcp.tool;
+
+public interface McpTool {}
diff --git a/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/mcp/tool/StackMcpTool.java b/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/mcp/tool/StackMcpTool.java
new file mode 100644
index 000000000..229d6b78d
--- /dev/null
+++ b/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/mcp/tool/StackMcpTool.java
@@ -0,0 +1,60 @@
+/*
+ * 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
+ *
+ * https://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.bigtop.manager.server.mcp.tool;
+
+import org.apache.bigtop.manager.server.mcp.converter.JsonToolCallResultConverter;
+import org.apache.bigtop.manager.server.model.converter.ServiceConverter;
+import org.apache.bigtop.manager.server.model.converter.StackConverter;
+import org.apache.bigtop.manager.server.model.dto.ServiceDTO;
+import org.apache.bigtop.manager.server.model.dto.StackDTO;
+import org.apache.bigtop.manager.server.model.vo.StackVO;
+import org.apache.bigtop.manager.server.utils.StackUtils;
+
+import org.springframework.ai.tool.annotation.Tool;
+import org.springframework.stereotype.Component;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+@Component
+public class StackMcpTool implements McpTool {
+
+ @Tool(
+ name = "ListStacks",
+ description = "List supported stacks",
+ resultConverter = JsonToolCallResultConverter.class)
+ public List listStacks() {
+ List stackVOList = new ArrayList<>();
+
+ for (Map.Entry> entry : StackUtils.STACK_SERVICE_MAP.entrySet()) {
+ StackDTO stackDTO = entry.getKey();
+ List serviceDTOList = entry.getValue();
+ for (ServiceDTO serviceDTO : serviceDTOList) {
+ serviceDTO.setConfigs(StackUtils.SERVICE_CONFIG_MAP.get(serviceDTO.getName()));
+ }
+
+ StackVO stackVO = StackConverter.INSTANCE.fromDTO2VO(stackDTO);
+ stackVO.setServices(ServiceConverter.INSTANCE.fromDTO2VO(serviceDTOList));
+ stackVOList.add(stackVO);
+ }
+
+ return stackVOList;
+ }
+}
diff --git a/bigtop-manager-server/src/main/resources/application.yml b/bigtop-manager-server/src/main/resources/application.yml
index 87b5dbd0f..860573b97 100644
--- a/bigtop-manager-server/src/main/resources/application.yml
+++ b/bigtop-manager-server/src/main/resources/application.yml
@@ -18,6 +18,13 @@
#
spring:
+ ai:
+ mcp:
+ server:
+ name: bigtop-manager-mcp-server
+ type: ASYNC
+ sse-endpoint: /mcp/sse
+ sse-message-endpoint: /mcp/messages
banner:
charset: utf-8
application:
diff --git a/bigtop-manager-server/src/main/resources/prompts/sample.prompt b/bigtop-manager-server/src/main/resources/prompts/sample.prompt
new file mode 100644
index 000000000..a5daf3b9e
--- /dev/null
+++ b/bigtop-manager-server/src/main/resources/prompts/sample.prompt
@@ -0,0 +1 @@
+sample prompt file
\ No newline at end of file