diff --git a/hawkbit-mcp-server/.gitattributes b/hawkbit-mcp-server/.gitattributes new file mode 100644 index 0000000000..3b41682ac5 --- /dev/null +++ b/hawkbit-mcp-server/.gitattributes @@ -0,0 +1,2 @@ +/mvnw text eol=lf +*.cmd text eol=crlf diff --git a/hawkbit-mcp-server/.gitignore b/hawkbit-mcp-server/.gitignore new file mode 100644 index 0000000000..667aaef0c8 --- /dev/null +++ b/hawkbit-mcp-server/.gitignore @@ -0,0 +1,33 @@ +HELP.md +target/ +.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ diff --git a/hawkbit-mcp-server/README.md b/hawkbit-mcp-server/README.md new file mode 100644 index 0000000000..2d303cc940 --- /dev/null +++ b/hawkbit-mcp-server/README.md @@ -0,0 +1,57 @@ +# hawkBit MCP Server + +This project provides an implementation of a **Model Context Protocol (MCP) server** +that exposes **Eclipse hawkBit** management capabilities to **intelligent agents**. + +--- + +## Configuration + +The MCP server is configured via `application.yaml`. + +Example configuration: + +```yaml +hawkbit: + server: + mgmt-url: http://localhost:8080 # hawkBit Management API URL + +server: + port: 8090 # MCP server port + +``` + +## Build + +This module is built independently from the main HawkBit services. + +Run the Maven build **from the `hawkbit-mcp-server` module directory only**: + +```bash +cd hawkbit-mcp-server +mvn clean package +``` + +This will generate the executable JAR under: +```bash +target/hawkbit-mcp-server-.jar +``` + +## MCP Client Integration + +To connect this server to an MCP client or Agent, register it in the client configuration. + +Example MCP client configuration: +```json +{ + "mcpServers": { + "hawkbit": { + "command": "java", + "args": [ + "-jar", + "/path/target/hawkbit-mcp-server-0.0.1-SNAPSHOT.jar" + ] + } + } +} +``` diff --git a/hawkbit-mcp-server/pom.xml b/hawkbit-mcp-server/pom.xml new file mode 100644 index 0000000000..bdbb805b40 --- /dev/null +++ b/hawkbit-mcp-server/pom.xml @@ -0,0 +1,65 @@ + + + 4.0.0 + + + org.eclipse.hawkbit + hawkbit-parent + ${revision} + + + hawkbit-mcp-server + ${revision} + hawkbit-mcp-server + MCP Server for Eclipse Hawkbit integration and management via LLM(intelligent agents). + + + 17 + 1.1.2 + + + + + org.springframework.ai + spring-ai-starter-mcp-server + + + + org.springframework.boot + spring-boot-starter-test + test + + + org.eclipse.hawkbit + hawkbit-sdk-mgmt + ${revision} + + + org.projectlombok + lombok + true + + + + + + org.springframework.ai + spring-ai-bom + ${spring-ai.version} + pom + import + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + diff --git a/hawkbit-mcp-server/src/main/java/org/eclipse/hawkbit/mcp/HawkbitMcpServerApplication.java b/hawkbit-mcp-server/src/main/java/org/eclipse/hawkbit/mcp/HawkbitMcpServerApplication.java new file mode 100644 index 0000000000..ee69599e46 --- /dev/null +++ b/hawkbit-mcp-server/src/main/java/org/eclipse/hawkbit/mcp/HawkbitMcpServerApplication.java @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2026 Bosch Software Innovations GmbH and others + * + * This program and the accompanying materials are made available under + * the terms of the Eclipse Public License 2.0 which is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + */ + +package org.eclipse.hawkbit.mcp; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class HawkbitMcpServerApplication { + + public static void main(String[] args) { + SpringApplication.run(HawkbitMcpServerApplication.class, args); + } + +} diff --git a/hawkbit-mcp-server/src/main/java/org/eclipse/hawkbit/mcp/config/HawkbitClientConfig.java b/hawkbit-mcp-server/src/main/java/org/eclipse/hawkbit/mcp/config/HawkbitClientConfig.java new file mode 100644 index 0000000000..f11b57e48a --- /dev/null +++ b/hawkbit-mcp-server/src/main/java/org/eclipse/hawkbit/mcp/config/HawkbitClientConfig.java @@ -0,0 +1,27 @@ +package org.eclipse.hawkbit.mcp.config; + +import org.eclipse.hawkbit.sdk.HawkbitClient; +import org.eclipse.hawkbit.sdk.HawkbitServer; +import org.eclipse.hawkbit.sdk.Tenant; +import org.eclipse.hawkbit.sdk.mgmt.AuthenticationSetupHelper; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import feign.Contract; +import feign.codec.Decoder; +import feign.codec.Encoder; + +@Configuration +public class HawkbitClientConfig { + + @Bean + public HawkbitClient hawkbitClient(final HawkbitServer hawkbitServer, final Encoder encoder, final Decoder decoder, + final Contract contract) { + return new HawkbitClient(hawkbitServer, encoder, decoder, contract); + } + + @Bean + AuthenticationSetupHelper mgmtApi(final Tenant tenant, final HawkbitClient hawkbitClient) { + return new AuthenticationSetupHelper(tenant, hawkbitClient); + } +} diff --git a/hawkbit-mcp-server/src/main/java/org/eclipse/hawkbit/mcp/feature/enumeration/Operator.java b/hawkbit-mcp-server/src/main/java/org/eclipse/hawkbit/mcp/feature/enumeration/Operator.java new file mode 100644 index 0000000000..8c467eeb55 --- /dev/null +++ b/hawkbit-mcp-server/src/main/java/org/eclipse/hawkbit/mcp/feature/enumeration/Operator.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2026 Bosch Software Innovations GmbH and others + * + * This program and the accompanying materials are made available under + * the terms of the Eclipse Public License 2.0 which is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + */ + +package org.eclipse.hawkbit.mcp.feature.enumeration; + +import lombok.Getter; + +@Getter +public enum Operator { + + EQUALS("==", OperatorType.COMPARISON), + NOT_EQUALS("!=", OperatorType.COMPARISON), + + GREATER_OR_EQUAL("=ge=", OperatorType.COMPARISON), + LESS_OR_EQUAL("=le=", OperatorType.COMPARISON), + + IN("=in=", OperatorType.COLLECTION), + OUT("=out=", OperatorType.COLLECTION), + + IS_NULL("=is=null", OperatorType.NULL_CHECK), + IS_NOT_NULL("=not=null", OperatorType.NULL_CHECK), + + AND(";", OperatorType.LOGICAL), + OR(",", OperatorType.LOGICAL); + + private final String symbol; + private final OperatorType type; + + Operator(String symbol, OperatorType type) { + this.symbol = symbol; + this.type = type; + } + + public static String documentation() { + return "OPS:\n== != =ge= =le= =in= =out= =is=null =not=null ; ,"; + } +} diff --git a/hawkbit-mcp-server/src/main/java/org/eclipse/hawkbit/mcp/feature/enumeration/OperatorType.java b/hawkbit-mcp-server/src/main/java/org/eclipse/hawkbit/mcp/feature/enumeration/OperatorType.java new file mode 100644 index 0000000000..8a25ceb2ad --- /dev/null +++ b/hawkbit-mcp-server/src/main/java/org/eclipse/hawkbit/mcp/feature/enumeration/OperatorType.java @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2026 Bosch Software Innovations GmbH and others + * + * This program and the accompanying materials are made available under + * the terms of the Eclipse Public License 2.0 which is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + */ + +package org.eclipse.hawkbit.mcp.feature.enumeration; + +public enum OperatorType { + COMPARISON, + LOGICAL, + NULL_CHECK, + COLLECTION +} \ No newline at end of file diff --git a/hawkbit-mcp-server/src/main/java/org/eclipse/hawkbit/mcp/feature/target/TargetFilterDoc.java b/hawkbit-mcp-server/src/main/java/org/eclipse/hawkbit/mcp/feature/target/TargetFilterDoc.java new file mode 100644 index 0000000000..d0beb06040 --- /dev/null +++ b/hawkbit-mcp-server/src/main/java/org/eclipse/hawkbit/mcp/feature/target/TargetFilterDoc.java @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2026 Bosch Software Innovations GmbH and others + * + * This program and the accompanying materials are made available under + * the terms of the Eclipse Public License 2.0 which is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + */ + +package org.eclipse.hawkbit.mcp.feature.target; + +import org.springaicommunity.mcp.annotation.McpResource; +import org.springframework.stereotype.Component; + +@Component +public class TargetFilterDoc { + + @McpResource(uri = "hawkbit://targets/filter", name = "Target Filter Schema", description = "Canonical schema for target filtering.", mimeType = "text/plain") + public String getTargetFilterSchema() { + return TargetFilterSchema.documentation(); + } +} diff --git a/hawkbit-mcp-server/src/main/java/org/eclipse/hawkbit/mcp/feature/target/TargetFilterSchema.java b/hawkbit-mcp-server/src/main/java/org/eclipse/hawkbit/mcp/feature/target/TargetFilterSchema.java new file mode 100644 index 0000000000..e8005cd6d2 --- /dev/null +++ b/hawkbit-mcp-server/src/main/java/org/eclipse/hawkbit/mcp/feature/target/TargetFilterSchema.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2026 Bosch Software Innovations GmbH and others + * + * This program and the accompanying materials are made available under + * the terms of the Eclipse Public License 2.0 which is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + */ + +package org.eclipse.hawkbit.mcp.feature.target; + +import org.eclipse.hawkbit.mcp.feature.enumeration.Operator; + +public final class TargetFilterSchema { + + public static final String FIELDS = """ + id,name,description,createdat,lastmodifiedat,controllerid,ipaddress,lastcontrollerrequestat,updatestatus + attribute. + metadata. + tag.name + targettype.key,targettype.name + assignedds.name,assignedds.version,installedds.name,installedds.version + """; + + public static String documentation() { + return """ + FIELDS: + %s + + %s + """.formatted(FIELDS, Operator.documentation()); + } +} diff --git a/hawkbit-mcp-server/src/main/java/org/eclipse/hawkbit/mcp/feature/target/TargetMcpService.java b/hawkbit-mcp-server/src/main/java/org/eclipse/hawkbit/mcp/feature/target/TargetMcpService.java new file mode 100644 index 0000000000..bd8acd9dab --- /dev/null +++ b/hawkbit-mcp-server/src/main/java/org/eclipse/hawkbit/mcp/feature/target/TargetMcpService.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2026 Bosch Software Innovations GmbH and others + * + * This program and the accompanying materials are made available under + * the terms of the Eclipse Public License 2.0 which is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + */ + +package org.eclipse.hawkbit.mcp.feature.target; + +import org.eclipse.hawkbit.mgmt.json.model.PagedList; +import org.eclipse.hawkbit.mgmt.json.model.target.MgmtTarget; +import org.eclipse.hawkbit.mgmt.rest.api.MgmtTargetRestApi; +import org.eclipse.hawkbit.sdk.HawkbitClient; +import org.eclipse.hawkbit.sdk.Tenant; +import org.springaicommunity.mcp.annotation.McpTool; +import org.springaicommunity.mcp.annotation.McpToolParam; +import org.springframework.stereotype.Service; + +@Service +public class TargetMcpService { + + private final MgmtTargetRestApi targetApi; + + public TargetMcpService(final HawkbitClient hawkbitClient, final Tenant tenant) { + this.targetApi = hawkbitClient.mgmtService(MgmtTargetRestApi.class, tenant); + } + + @McpTool(name = "listTargets", description = """ + Search for targets (devices) with pagination. + Supports FIQL filters. + + Filter schema: + hawkbit://targets/filter + """) + PagedList getTargets( + @McpToolParam(description = "FIQL filter expression. Example: updatestatus==ERROR", required = false) String rsqlParam, + + @McpToolParam(description = "Page offset (zero-based).", required = true) int offset, + + @McpToolParam(description = "Page size (max 50).", required = true) int size, + + @McpToolParam(description = "Sort parameter. Example: name:asc.", required = false) String sortParam) { + return targetApi.getTargets(rsqlParam, offset, size, sortParam).getBody(); + } + +} diff --git a/hawkbit-mcp-server/src/main/resources/application.yaml b/hawkbit-mcp-server/src/main/resources/application.yaml new file mode 100644 index 0000000000..ffdc0bf2f6 --- /dev/null +++ b/hawkbit-mcp-server/src/main/resources/application.yaml @@ -0,0 +1,26 @@ +spring: + application: + name: hawkbit-mcp-server + + ai: + mcp: + server: + name: hawkbit-mcp-server + version: 0.0.1 + type: SYNC + annotation-scanner: + enabled: true + main: + web-application-type: NONE + banner-mode: OFF + +logging: + level: + root: OFF + +hawkbit: + server: + mgmt-url: http://localhost:8080 + +server: + port: 8090 diff --git a/pom.xml b/pom.xml index 43125a1201..190db80f98 100644 --- a/pom.xml +++ b/pom.xml @@ -731,5 +731,7 @@ hawkbit-ui hawkbit-sdk + + hawkbit-mcp-server