From 69fdfc737f9bd2670d37ac856d562a307c2403f2 Mon Sep 17 00:00:00 2001 From: Romulohenriquesc Date: Sun, 18 Jan 2026 14:31:00 -0300 Subject: [PATCH 1/3] Add initial MCP server module for HawkBit --- hawkbit-mcp-server/.gitattributes | 2 + hawkbit-mcp-server/.gitignore | 33 ++++++++++ hawkbit-mcp-server/README.md | 54 +++++++++++++++ hawkbit-mcp-server/pom.xml | 65 +++++++++++++++++++ .../mcp/HawkbitMcpServerApplication.java | 13 ++++ .../mcp/config/HawkbitClientConfig.java | 27 ++++++++ .../mcp/feature/enumeration/Operator.java | 34 ++++++++++ .../mcp/feature/enumeration/OperatorType.java | 8 +++ .../mcp/feature/target/TargetFilterDoc.java | 13 ++++ .../feature/target/TargetFilterSchema.java | 24 +++++++ .../mcp/feature/target/TargetMcpService.java | 39 +++++++++++ .../src/main/resources/application.yaml | 26 ++++++++ pom.xml | 2 + 13 files changed, 340 insertions(+) create mode 100644 hawkbit-mcp-server/.gitattributes create mode 100644 hawkbit-mcp-server/.gitignore create mode 100644 hawkbit-mcp-server/README.md create mode 100644 hawkbit-mcp-server/pom.xml create mode 100644 hawkbit-mcp-server/src/main/java/org/eclipse/hawkbit/mcp/HawkbitMcpServerApplication.java create mode 100644 hawkbit-mcp-server/src/main/java/org/eclipse/hawkbit/mcp/config/HawkbitClientConfig.java create mode 100644 hawkbit-mcp-server/src/main/java/org/eclipse/hawkbit/mcp/feature/enumeration/Operator.java create mode 100644 hawkbit-mcp-server/src/main/java/org/eclipse/hawkbit/mcp/feature/enumeration/OperatorType.java create mode 100644 hawkbit-mcp-server/src/main/java/org/eclipse/hawkbit/mcp/feature/target/TargetFilterDoc.java create mode 100644 hawkbit-mcp-server/src/main/java/org/eclipse/hawkbit/mcp/feature/target/TargetFilterSchema.java create mode 100644 hawkbit-mcp-server/src/main/java/org/eclipse/hawkbit/mcp/feature/target/TargetMcpService.java create mode 100644 hawkbit-mcp-server/src/main/resources/application.yaml 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..eb5cbcabb3 --- /dev/null +++ b/hawkbit-mcp-server/README.md @@ -0,0 +1,54 @@ +# 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 + +To build the project using Maven: + +```bash +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..611e7a899f --- /dev/null +++ b/hawkbit-mcp-server/src/main/java/org/eclipse/hawkbit/mcp/HawkbitMcpServerApplication.java @@ -0,0 +1,13 @@ +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..874b8a3ca8 --- /dev/null +++ b/hawkbit-mcp-server/src/main/java/org/eclipse/hawkbit/mcp/feature/enumeration/Operator.java @@ -0,0 +1,34 @@ +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..a5f2399721 --- /dev/null +++ b/hawkbit-mcp-server/src/main/java/org/eclipse/hawkbit/mcp/feature/enumeration/OperatorType.java @@ -0,0 +1,8 @@ +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..a843ac2ad8 --- /dev/null +++ b/hawkbit-mcp-server/src/main/java/org/eclipse/hawkbit/mcp/feature/target/TargetFilterDoc.java @@ -0,0 +1,13 @@ +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..b2e4ea4914 --- /dev/null +++ b/hawkbit-mcp-server/src/main/java/org/eclipse/hawkbit/mcp/feature/target/TargetFilterSchema.java @@ -0,0 +1,24 @@ +package org.eclipse.hawkbit.mcp.feature.target; + +import org.eclipse.hawkbit.mcp.feature.enumeration.Operator; + +public 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..2077a40f36 --- /dev/null +++ b/hawkbit-mcp-server/src/main/java/org/eclipse/hawkbit/mcp/feature/target/TargetMcpService.java @@ -0,0 +1,39 @@ +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 From fb678b25acf8c288a35fdce2d8bc92319df18cd3 Mon Sep 17 00:00:00 2001 From: Romulohenriquesc Date: Sun, 18 Jan 2026 14:39:32 -0300 Subject: [PATCH 2/3] feat: Updated build instructions in the README. --- hawkbit-mcp-server/README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/hawkbit-mcp-server/README.md b/hawkbit-mcp-server/README.md index eb5cbcabb3..2d303cc940 100644 --- a/hawkbit-mcp-server/README.md +++ b/hawkbit-mcp-server/README.md @@ -23,9 +23,12 @@ server: ## Build -To build the project using Maven: +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 ``` From 62b8e200b7e20e83c4a0c120ea792a5effed5728 Mon Sep 17 00:00:00 2001 From: Romulohenriquesc Date: Sun, 18 Jan 2026 15:31:18 -0300 Subject: [PATCH 3/3] Add EPL-2.0 license headers to MCP server module Signed-off-by: Romulohenriquesc --- .../hawkbit/mcp/HawkbitMcpServerApplication.java | 10 ++++++++++ .../hawkbit/mcp/feature/enumeration/Operator.java | 10 ++++++++++ .../mcp/feature/enumeration/OperatorType.java | 10 ++++++++++ .../hawkbit/mcp/feature/target/TargetFilterDoc.java | 10 ++++++++++ .../mcp/feature/target/TargetFilterSchema.java | 12 +++++++++++- .../hawkbit/mcp/feature/target/TargetMcpService.java | 10 ++++++++++ 6 files changed, 61 insertions(+), 1 deletion(-) 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 index 611e7a899f..ee69599e46 100644 --- 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 @@ -1,3 +1,13 @@ +/* + * 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; 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 index 874b8a3ca8..8c467eeb55 100644 --- 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 @@ -1,3 +1,13 @@ +/* + * 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; 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 index a5f2399721..8a25ceb2ad 100644 --- 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 @@ -1,3 +1,13 @@ +/* + * 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 { 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 index a843ac2ad8..d0beb06040 100644 --- 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 @@ -1,3 +1,13 @@ +/* + * 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; 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 index b2e4ea4914..e8005cd6d2 100644 --- 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 @@ -1,8 +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.target; import org.eclipse.hawkbit.mcp.feature.enumeration.Operator; -public class TargetFilterSchema { +public final class TargetFilterSchema { public static final String FIELDS = """ id,name,description,createdat,lastmodifiedat,controllerid,ipaddress,lastcontrollerrequestat,updatestatus 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 index 2077a40f36..bd8acd9dab 100644 --- 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 @@ -1,3 +1,13 @@ +/* + * 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;