diff --git a/docker/build/build_dev.sh b/docker/build/build_dev.sh
index 2da820bf7d..98efacfd57 100755
--- a/docker/build/build_dev.sh
+++ b/docker/build/build_dev.sh
@@ -62,6 +62,8 @@ if [ -z "$1" ]; then
build "hawkbit-update-server"
# db init
build "hawkbit-repository-jpa-init"
+ # mcp server
+ build "hawkbit-mcp-server"
else
echo "Build $1"
build $1
diff --git a/hawkbit-mcp/README.md b/hawkbit-mcp/README.md
new file mode 100644
index 0000000000..54e78db61f
--- /dev/null
+++ b/hawkbit-mcp/README.md
@@ -0,0 +1,109 @@
+# hawkBit MCP Server
+
+A standalone [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) server that provides AI assistants with tools to interact with [Eclipse hawkBit](https://www.eclipse.org/hawkbit/) for IoT device software update management.
+
+## Building
+
+From the project root directory:
+
+```bash
+mvn clean package -pl hawkbit-mcp -am -DskipTests
+```
+
+The JAR will be created at: `hawkbit-mcp/target/hawkbit-mcp-server-0-SNAPSHOT.jar`
+
+## Configuration
+
+The MCP server supports two transport modes:
+
+| Mode | Use Case | Authentication |
+|------|----------|----------------|
+| **HTTP/SSE** | Remote access, multi-user | Per-request via `Authorization` header |
+| **STDIO** | Local CLI tools (e.g., Claude Code) | Environment variables |
+
+
+### HTTP Transport
+
+Use HTTP transport when running the server as a standalone service:
+
+```json
+{
+ "mcpServers": {
+ "hawkbit-mcp": {
+ "type": "http",
+ "url": "http://localhost:8081/mcp",
+ "headers": {
+ "Authorization": "Basic "
+ }
+ }
+ }
+}
+```
+
+Start the server separately:
+
+```bash
+java -jar hawkbit-mcp-server-0-SNAPSHOT.jar \
+ --hawkbit.mcp.mgmt-url=
+```
+
+**Generating Base64 credentials:**
+
+```bash
+# Linux/Mac
+echo -n "\\:" | base64
+
+# PowerShell
+[Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes("\:"))
+```
+
+### STDIO Transport
+
+Use STDIO transport for direct integration:
+
+```json
+{
+ "mcpServers": {
+ "hawkbit-mcp": {
+ "command": "java",
+ "args": [
+ "-Dspring.ai.mcp.server.stdio=true",
+ "-Dspring.main.web-application-type=none",
+ "-jar",
+ "/path/to/hawkbit-mcp-server-0-SNAPSHOT.jar"
+ ],
+ "env": {
+ "HAWKBIT_URL": "",
+ "HAWKBIT_USERNAME": "\\",
+ "HAWKBIT_PASSWORD": ""
+ }
+ }
+ }
+}
+```
+
+## Configuration Properties
+
+| Property | Environment Variable | Description | Default |
+|----------|---------------------|-------------|---------|
+| `hawkbit.mcp.mgmt-url` | `HAWKBIT_URL` | hawkBit Management API URL | `http://localhost:8080` |
+| `hawkbit.mcp.username` | `HAWKBIT_USERNAME` | Username for STDIO mode | - |
+| `hawkbit.mcp.password` | `HAWKBIT_PASSWORD` | Password for STDIO mode | - |
+| `hawkbit.mcp.validation.enabled` | - | Validate credentials against hawkBit | `true` |
+| `hawkbit.mcp.validation.cache-ttl` | - | Cache TTL for auth validation | `600s` |
+
+### Operation Controls
+
+You can enable/disable specific operations globally or per-entity:
+
+```properties
+# Global: disable all deletes
+hawkbit.mcp.operations.delete-enabled=false
+
+# Per-entity: allow delete for targets only
+hawkbit.mcp.operations.targets.delete-enabled=true
+
+# Disable rollout lifecycle operations
+hawkbit.mcp.operations.rollouts.start-enabled=false
+hawkbit.mcp.operations.rollouts.approve-enabled=false
+```
diff --git a/hawkbit-mcp/pom.xml b/hawkbit-mcp/pom.xml
new file mode 100644
index 0000000000..089263b3e3
--- /dev/null
+++ b/hawkbit-mcp/pom.xml
@@ -0,0 +1,130 @@
+
+
+ 4.0.0
+
+ org.eclipse.hawkbit
+ hawkbit-parent
+ ${revision}
+
+
+ hawkbit-mcp-server
+ hawkBit :: MCP Server (Standalone)
+ Standalone MCP server that connects to hawkBit via REST API
+
+
+
+ org.eclipse.hawkbit
+ hawkbit-sdk-mgmt
+ ${project.version}
+
+
+ org.springdoc
+ springdoc-openapi-starter-webmvc-ui
+
+
+ org.springframework.boot
+ spring-boot-starter-hateoas
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-validation
+
+
+ org.springframework.boot
+ spring-boot-starter-security
+
+
+
+ org.springframework.ai
+ spring-ai-starter-mcp-server-webmvc
+
+
+ org.springframework.ai
+ spring-ai-mcp-annotations
+
+
+
+ org.springframework.boot
+ spring-boot-configuration-processor
+ true
+
+
+
+ com.github.ben-manes.caffeine
+ caffeine
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-resources-plugin
+
+
+ copy-hawkbit-docs
+ generate-resources
+
+ copy-resources
+
+
+ ${project.build.outputDirectory}/hawkbit-docs
+
+
+ ${project.basedir}/../docs
+
+ README.md
+ what-is-hawkbit.md
+ quick-start.md
+ features.md
+ architecture.md
+ base-setup.md
+ hawkbit-sdk.md
+ feign-client.md
+ clustering.md
+ authentication.md
+ authorization.md
+ datamodel.md
+ rollout-management.md
+ targetstate.md
+ management-api.md
+ direct-device-integration-api.md
+ device-management-federation-api.md
+
+
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+ org.eclipse.hawkbit.mcp.server.HawkbitMcpServerApplication
+
+
+
+
+ repackage
+
+
+
+
+
+
+
diff --git a/hawkbit-mcp/src/main/java/org/eclipse/hawkbit/mcp/server/HawkbitMcpServerApplication.java b/hawkbit-mcp/src/main/java/org/eclipse/hawkbit/mcp/server/HawkbitMcpServerApplication.java
new file mode 100644
index 0000000000..eb3c7c6746
--- /dev/null
+++ b/hawkbit-mcp/src/main/java/org/eclipse/hawkbit/mcp/server/HawkbitMcpServerApplication.java
@@ -0,0 +1,32 @@
+/**
+ * Copyright (c) 2026 Contributors to the Eclipse Foundation
+ *
+ * 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.server;
+
+import org.eclipse.hawkbit.mcp.server.config.HawkbitMcpProperties;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration;
+
+/**
+ * Standalone MCP Server application that connects to hawkBit via REST API.
+ *
+ * This server acts as a proxy between MCP clients and hawkBit,
+ * passing through authentication credentials to the hawkBit REST API.
+ *
+ */
+@SpringBootApplication(exclude = UserDetailsServiceAutoConfiguration.class)
+@EnableConfigurationProperties(HawkbitMcpProperties.class)
+public class HawkbitMcpServerApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(HawkbitMcpServerApplication.class, args);
+ }
+}
diff --git a/hawkbit-mcp/src/main/java/org/eclipse/hawkbit/mcp/server/client/HawkbitAuthenticationValidator.java b/hawkbit-mcp/src/main/java/org/eclipse/hawkbit/mcp/server/client/HawkbitAuthenticationValidator.java
new file mode 100644
index 0000000000..83c69dc510
--- /dev/null
+++ b/hawkbit-mcp/src/main/java/org/eclipse/hawkbit/mcp/server/client/HawkbitAuthenticationValidator.java
@@ -0,0 +1,157 @@
+/**
+ * Copyright (c) 2026 Contributors to the Eclipse Foundation
+ *
+ * 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.server.client;
+
+import com.github.benmanes.caffeine.cache.Cache;
+import com.github.benmanes.caffeine.cache.Caffeine;
+import feign.FeignException;
+import lombok.extern.slf4j.Slf4j;
+import org.eclipse.hawkbit.mcp.server.config.HawkbitMcpProperties;
+import org.eclipse.hawkbit.mgmt.rest.api.MgmtTenantManagementRestApi;
+import org.eclipse.hawkbit.sdk.HawkbitClient;
+import org.eclipse.hawkbit.sdk.Tenant;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Component;
+
+import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.HexFormat;
+
+/**
+ * Validates authentication credentials against hawkBit REST API using the SDK.
+ */
+@Slf4j
+@Component
+public class HawkbitAuthenticationValidator {
+
+ private final HawkbitClient hawkbitClient;
+ private final Tenant dummyTenant;
+ private final Cache validationCache;
+ private final boolean enabled;
+
+ public HawkbitAuthenticationValidator(final HawkbitClient hawkbitClient,
+ final Tenant dummyTenant,
+ final HawkbitMcpProperties properties) {
+ this.hawkbitClient = hawkbitClient;
+ this.dummyTenant = dummyTenant;
+ this.enabled = properties.getValidation().isEnabled();
+
+ this.validationCache = Caffeine.newBuilder()
+ .expireAfterWrite(properties.getValidation().getCacheTtl())
+ .maximumSize(properties.getValidation().getCacheMaxSize())
+ .build();
+
+ log.info("Authentication validation {} with cache TTL={}, maxSize={}",
+ enabled ? "enabled" : "disabled",
+ properties.getValidation().getCacheTtl(),
+ properties.getValidation().getCacheMaxSize());
+ }
+
+ /**
+ * Validates the given authorization header against hawkBit.
+ * @param authHeader the Authorization header value
+ * @return validation result
+ */
+ public ValidationResult validate(final String authHeader) {
+ if (!enabled) {
+ return ValidationResult.VALID;
+ }
+
+ if (authHeader == null || authHeader.isBlank()) {
+ return ValidationResult.MISSING_CREDENTIALS;
+ }
+
+ String cacheKey = hashAuthHeader(authHeader);
+ Boolean cachedResult = validationCache.getIfPresent(cacheKey);
+
+ if (cachedResult != null) {
+ log.debug("Authentication validation cache hit: valid={}", cachedResult);
+ return cachedResult ? ValidationResult.VALID : ValidationResult.INVALID_CREDENTIALS;
+ }
+
+ return validateWithHawkbit(cacheKey);
+ }
+
+ private ValidationResult validateWithHawkbit(final String cacheKey) {
+ log.debug("Validating authentication against hawkBit using SDK");
+
+ try {
+ MgmtTenantManagementRestApi tenantApi = hawkbitClient.mgmtService(
+ MgmtTenantManagementRestApi.class, dummyTenant);
+
+ ResponseEntity> response = tenantApi.getTenantConfiguration();
+ int statusCode = response.getStatusCode().value();
+
+ if (statusCode >= 200 && statusCode < 300) {
+ log.debug("Authentication valid (status={})", statusCode);
+ validationCache.put(cacheKey, true);
+ return ValidationResult.VALID;
+ } else {
+ log.warn("Unexpected status from hawkBit during auth validation: {}", statusCode);
+ return ValidationResult.HAWKBIT_ERROR;
+ }
+ } catch (FeignException.Unauthorized e) {
+ log.debug("Authentication invalid (status=401)");
+ validationCache.put(cacheKey, false);
+ return ValidationResult.INVALID_CREDENTIALS;
+ } catch (FeignException.Forbidden e) {
+ // 403 = Valid credentials but lacks READ_TENANT_CONFIGURATION permission
+ // User is authenticated in hawkBit but doesn't have this specific permission
+ log.debug("Authentication valid but lacks permission (status=403)");
+ validationCache.put(cacheKey, true);
+ return ValidationResult.VALID;
+ } catch (FeignException e) {
+ log.warn("Error validating authentication against hawkBit: {} - {}",
+ e.getClass().getSimpleName(), e.getMessage());
+ return ValidationResult.HAWKBIT_ERROR;
+ } catch (Exception e) {
+ // Unexpected errors, don't cache, fail closed
+ log.warn("Unexpected error validating authentication against hawkBit: {}", e.getMessage());
+ return ValidationResult.HAWKBIT_ERROR;
+ }
+ }
+
+ private String hashAuthHeader(final String authHeader) {
+ try {
+ MessageDigest digest = MessageDigest.getInstance("SHA-256");
+ byte[] hash = digest.digest(authHeader.getBytes(StandardCharsets.UTF_8));
+ return HexFormat.of().formatHex(hash);
+ } catch (NoSuchAlgorithmException e) {
+ // SHA-256 is always available
+ throw new McpAuthenticationException("SHA-256 not available." + e.getMessage());
+ }
+ }
+
+ /**
+ * Result of authentication validation.
+ */
+ public enum ValidationResult {
+ /**
+ * Credentials are valid (authenticated user).
+ */
+ VALID,
+
+ /**
+ * No credentials provided.
+ */
+ MISSING_CREDENTIALS,
+
+ /**
+ * Credentials are invalid (401 from hawkBit).
+ */
+ INVALID_CREDENTIALS,
+
+ /**
+ * hawkBit is unavailable or returned unexpected error.
+ */
+ HAWKBIT_ERROR
+ }
+}
diff --git a/hawkbit-mcp/src/main/java/org/eclipse/hawkbit/mcp/server/client/McpAuthenticationException.java b/hawkbit-mcp/src/main/java/org/eclipse/hawkbit/mcp/server/client/McpAuthenticationException.java
new file mode 100644
index 0000000000..9f07ceb06b
--- /dev/null
+++ b/hawkbit-mcp/src/main/java/org/eclipse/hawkbit/mcp/server/client/McpAuthenticationException.java
@@ -0,0 +1,17 @@
+/**
+ * Copyright (c) 2026 Contributors to the Eclipse Foundation
+ *
+ * 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.server.client;
+
+public class McpAuthenticationException extends RuntimeException {
+
+ public McpAuthenticationException(String message) {
+ super(message);
+ }
+}
diff --git a/hawkbit-mcp/src/main/java/org/eclipse/hawkbit/mcp/server/config/HawkbitClientConfiguration.java b/hawkbit-mcp/src/main/java/org/eclipse/hawkbit/mcp/server/config/HawkbitClientConfiguration.java
new file mode 100644
index 0000000000..4809331a3d
--- /dev/null
+++ b/hawkbit-mcp/src/main/java/org/eclipse/hawkbit/mcp/server/config/HawkbitClientConfiguration.java
@@ -0,0 +1,120 @@
+/**
+ * Copyright (c) 2026 Contributors to the Eclipse Foundation
+ *
+ * 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.server.config;
+
+import feign.Contract;
+import feign.RequestInterceptor;
+import feign.codec.Decoder;
+import feign.codec.Encoder;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.eclipse.hawkbit.sdk.Controller;
+import org.eclipse.hawkbit.sdk.HawkbitClient;
+import org.eclipse.hawkbit.sdk.HawkbitServer;
+import org.eclipse.hawkbit.sdk.Tenant;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Primary;
+import org.springframework.http.HttpHeaders;
+import org.springframework.web.context.request.RequestAttributes;
+import org.springframework.web.context.request.RequestContextHolder;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Base64;
+import java.util.function.BiFunction;
+
+/**
+ * Configuration for the hawkBit SDK client.
+ *
+ * Uses a custom request interceptor to inject authentication from the
+ * current HTTP request context (HTTP mode) or from static credentials (STDIO mode).
+ *
+ */
+@Slf4j
+@Configuration
+@RequiredArgsConstructor
+public class HawkbitClientConfiguration {
+
+ private final HawkbitMcpProperties properties;
+
+ @Bean
+ @Primary
+ public HawkbitServer hawkbitServer() {
+ HawkbitServer server = new HawkbitServer();
+ server.setMgmtUrl(properties.getMgmtUrl());
+ log.info("Configured hawkBit server URL: {}", properties.getMgmtUrl());
+ return server;
+ }
+
+ @Bean
+ public HawkbitClient hawkbitClient(final HawkbitServer server,
+ final Encoder encoder,
+ final Decoder decoder,
+ final Contract contract) {
+ return HawkbitClient.builder()
+ .hawkBitServer(server)
+ .encoder(encoder)
+ .decoder(decoder)
+ .contract(contract)
+ .requestInterceptorFn(requestContextInterceptor())
+ .build();
+ }
+
+ private BiFunction requestContextInterceptor() {
+ return (tenant, controller) -> template -> {
+ String authHeader = null;
+
+ // Try request context first (HTTP mode)
+ RequestAttributes attrs = RequestContextHolder.getRequestAttributes();
+ if (attrs != null) {
+ authHeader = (String) attrs.getAttribute(
+ McpSecurityConfiguration.AUTH_HEADER_ATTRIBUTE,
+ RequestAttributes.SCOPE_REQUEST);
+ if (authHeader != null) {
+ log.trace("Using auth header from HTTP request context");
+ }
+ }
+
+ // Fall back to static credentials (STDIO mode)
+ if (authHeader == null && properties.hasStaticCredentials()) {
+ String credentials = properties.getUsername() + ":" + properties.getPassword();
+ authHeader = "Basic " + Base64.getEncoder().encodeToString(
+ credentials.getBytes(StandardCharsets.UTF_8));
+ log.trace("Using static credentials from properties (STDIO mode)");
+ }
+
+ // Apply header if available
+ if (authHeader != null) {
+ template.header(HttpHeaders.AUTHORIZATION, authHeader);
+ } else {
+ log.warn("No authentication available - request will likely fail");
+ }
+ };
+ }
+
+ /**
+ * Tenant bean - uses static credentials if configured (STDIO mode),
+ * otherwise actual authentication comes from request context via interceptor (HTTP mode).
+ */
+ @Bean
+ public Tenant dummyTenant() {
+ Tenant tenant = new Tenant();
+ if (properties.hasStaticCredentials()) {
+ tenant.setUsername(properties.getUsername());
+ tenant.setPassword(properties.getPassword());
+ log.info("Configured tenant with static credentials (STDIO mode)");
+ } else {
+ tenant.setUsername(null);
+ tenant.setPassword(null);
+ log.info("Configured tenant without static credentials (HTTP mode - per-request auth)");
+ }
+ return tenant;
+ }
+}
diff --git a/hawkbit-mcp/src/main/java/org/eclipse/hawkbit/mcp/server/config/HawkbitMcpProperties.java b/hawkbit-mcp/src/main/java/org/eclipse/hawkbit/mcp/server/config/HawkbitMcpProperties.java
new file mode 100644
index 0000000000..1b7ae875fe
--- /dev/null
+++ b/hawkbit-mcp/src/main/java/org/eclipse/hawkbit/mcp/server/config/HawkbitMcpProperties.java
@@ -0,0 +1,221 @@
+/**
+ * Copyright (c) 2026 Contributors to the Eclipse Foundation
+ *
+ * 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.server.config;
+
+import jakarta.validation.constraints.NotBlank;
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.validation.annotation.Validated;
+
+import java.time.Duration;
+
+/**
+ * Configuration properties for the standalone hawkBit MCP server.
+ */
+@Data
+@Validated
+@ConfigurationProperties(prefix = "hawkbit.mcp")
+public class HawkbitMcpProperties {
+
+ /**
+ * Base URL of the hawkBit Management API (e.g., ...).
+ */
+ @NotBlank(message = "hawkbit.mcp.mgmt-url must be configured")
+ private String mgmtUrl;
+
+ /**
+ * Username for hawkBit authentication (used in STDIO mode).
+ * Format: {@code tenant\\username} or just username depending on hawkBit configuration.
+ * Can be set via HAWKBIT_USERNAME environment variable.
+ */
+ private String username;
+
+ /**
+ * Password for hawkBit authentication (used in STDIO mode).
+ * Can be set via HAWKBIT_PASSWORD environment variable.
+ */
+ private String password;
+
+ /**
+ * Check if static credentials are configured.
+ */
+ public boolean hasStaticCredentials() {
+ return username != null && !username.isBlank()
+ && password != null && !password.isBlank();
+ }
+
+ /**
+ * Whether to enable the built-in hawkBit tools.
+ * Set to false to provide custom tool implementations.
+ */
+ private boolean toolsEnabled = true;
+
+ /**
+ * Whether to enable the built-in hawkBit documentation resources.
+ * Set to false to provide custom resource implementations.
+ */
+ private boolean resourcesEnabled = true;
+
+ /**
+ * Whether to enable the built-in hawkBit prompts.
+ * Set to false to provide custom prompt implementations.
+ */
+ private boolean promptsEnabled = true;
+
+ /**
+ * Authentication validation configuration.
+ */
+ private Validation validation = new Validation();
+
+ /**
+ * Operations configuration for enabling/disabling specific operations.
+ */
+ private Operations operations = new Operations();
+
+ /**
+ * Configuration for pre-authentication validation against hawkBit.
+ */
+ @Data
+ public static class Validation {
+
+ /**
+ * Whether to validate authentication against hawkBit before processing MCP requests.
+ */
+ private boolean enabled = true;
+
+ /**
+ * Duration to cache authentication validation results.
+ * Shorter values are more secure but increase load on hawkBit.
+ */
+ private Duration cacheTtl = Duration.ofSeconds(60);
+
+ /**
+ * Maximum number of entries in the authentication validation cache.
+ */
+ private int cacheMaxSize = 1000;
+ }
+
+ /**
+ * Configuration for enabling/disabling operations at global and per-entity levels.
+ */
+ @Data
+ public static class Operations {
+
+ // Global defaults
+ private boolean listEnabled = true;
+ private boolean createEnabled = true;
+ private boolean updateEnabled = true;
+ private boolean deleteEnabled = true;
+
+ // Per-entity overrides (null = use global)
+ private EntityConfig targets = new EntityConfig();
+ private RolloutConfig rollouts = new RolloutConfig();
+ private EntityConfig distributionSets = new EntityConfig();
+ private ActionConfig actions = new ActionConfig();
+ private EntityConfig softwareModules = new EntityConfig();
+ private EntityConfig targetFilters = new EntityConfig();
+
+ /**
+ * Check if an operation is enabled globally.
+ */
+ public boolean isGlobalOperationEnabled(final String operation) {
+ return switch (operation.toLowerCase()) {
+ case "list" -> listEnabled;
+ case "create" -> createEnabled;
+ case "update" -> updateEnabled;
+ case "delete" -> deleteEnabled;
+ default -> true;
+ };
+ }
+ }
+
+ /**
+ * Per-entity operation configuration.
+ */
+ @Data
+ public static class EntityConfig {
+
+ private Boolean listEnabled;
+ private Boolean createEnabled;
+ private Boolean updateEnabled;
+ private Boolean deleteEnabled;
+
+ /**
+ * Get the enabled state for an operation, or null if not set (use global).
+ */
+ public Boolean getOperationEnabled(final String operation) {
+ return switch (operation.toLowerCase()) {
+ case "list" -> listEnabled;
+ case "create" -> createEnabled;
+ case "update" -> updateEnabled;
+ case "delete" -> deleteEnabled;
+ default -> null;
+ };
+ }
+ }
+
+ /**
+ * Rollout-specific operation configuration including lifecycle operations.
+ */
+ @Data
+ public static class RolloutConfig extends EntityConfig {
+
+ private Boolean startEnabled = true;
+ private Boolean pauseEnabled = true;
+ private Boolean stopEnabled = true;
+ private Boolean resumeEnabled = true;
+ private Boolean approveEnabled = true;
+ private Boolean denyEnabled = true;
+ private Boolean retryEnabled = true;
+ private Boolean triggerNextGroupEnabled = true;
+
+ @Override
+ public Boolean getOperationEnabled(final String operation) {
+ final Boolean baseResult = super.getOperationEnabled(operation);
+ if (baseResult != null) {
+ return baseResult;
+ }
+ return switch (operation.toLowerCase().replace("_", "-")) {
+ case "start" -> startEnabled;
+ case "pause" -> pauseEnabled;
+ case "stop" -> stopEnabled;
+ case "resume" -> resumeEnabled;
+ case "approve" -> approveEnabled;
+ case "deny" -> denyEnabled;
+ case "retry" -> retryEnabled;
+ case "trigger-next-group" -> triggerNextGroupEnabled;
+ default -> null;
+ };
+ }
+ }
+
+ /**
+ * Action-specific operation configuration.
+ */
+ @Data
+ public static class ActionConfig {
+
+ private Boolean listEnabled;
+ private Boolean deleteEnabled;
+ private Boolean deleteBatchEnabled = true;
+
+ /**
+ * Get the enabled state for an operation, or null if not set (use global).
+ */
+ public Boolean getOperationEnabled(final String operation) {
+ return switch (operation.toLowerCase().replace("_", "-")) {
+ case "list" -> listEnabled;
+ case "delete" -> deleteEnabled;
+ case "delete-batch" -> deleteBatchEnabled;
+ default -> null;
+ };
+ }
+ }
+}
diff --git a/hawkbit-mcp/src/main/java/org/eclipse/hawkbit/mcp/server/config/McpSecurityConfiguration.java b/hawkbit-mcp/src/main/java/org/eclipse/hawkbit/mcp/server/config/McpSecurityConfiguration.java
new file mode 100644
index 0000000000..b900fba9af
--- /dev/null
+++ b/hawkbit-mcp/src/main/java/org/eclipse/hawkbit/mcp/server/config/McpSecurityConfiguration.java
@@ -0,0 +1,128 @@
+/**
+ * Copyright (c) 2026 Contributors to the Eclipse Foundation
+ *
+ * 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.server.config;
+
+import jakarta.servlet.FilterChain;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import lombok.NonNull;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.eclipse.hawkbit.mcp.server.client.HawkbitAuthenticationValidator;
+import org.eclipse.hawkbit.mcp.server.client.HawkbitAuthenticationValidator.ValidationResult;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
+import org.springframework.security.config.http.SessionCreationPolicy;
+import org.springframework.security.web.SecurityFilterChain;
+import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
+import org.springframework.web.filter.OncePerRequestFilter;
+
+import java.io.IOException;
+
+/**
+ * Security configuration for the MCP server.
+ *
+ * This configuration is only active in HTTP/servlet mode. In STDIO mode,
+ * authentication is handled via static credentials from properties.
+ *
+ */
+@Slf4j
+@Configuration
+@EnableWebSecurity
+@RequiredArgsConstructor
+@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
+public class McpSecurityConfiguration {
+
+ /**
+ * Request attribute key for storing the Authorization header.
+ */
+ public static final String AUTH_HEADER_ATTRIBUTE = "hawkbit.mcp.auth.header";
+
+ private final HawkbitAuthenticationValidator authenticationValidator;
+
+ @Bean
+ @SuppressWarnings("java:S4502") // CSRF protection is not needed for stateless REST APIs using Authorization header
+ public SecurityFilterChain mcpSecurityFilterChain(final HttpSecurity http) throws Exception {
+ http
+ .authorizeHttpRequests(auth -> auth.anyRequest().permitAll())
+ .csrf(AbstractHttpConfigurer::disable)
+ .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
+ .addFilterBefore(new HawkBitAuthenticationFilter(authenticationValidator),
+ UsernamePasswordAuthenticationFilter.class);
+
+ return http.build();
+ }
+
+ /**
+ * Filter that validates authentication.
+ */
+ @Slf4j
+ @RequiredArgsConstructor
+ public static class HawkBitAuthenticationFilter extends OncePerRequestFilter {
+
+ private final HawkbitAuthenticationValidator validator;
+
+ @Override
+ protected void doFilterInternal(final HttpServletRequest request, final @NonNull HttpServletResponse response,
+ final @NonNull FilterChain filterChain) throws ServletException, IOException {
+ String authHeader = request.getHeader(HttpHeaders.AUTHORIZATION);
+
+ if (authHeader != null) {
+ request.setAttribute(AUTH_HEADER_ATTRIBUTE, authHeader);
+ }
+
+ ValidationResult result = validator.validate(authHeader);
+
+ switch (result) {
+ case VALID:
+ filterChain.doFilter(request, response);
+ break;
+
+ case MISSING_CREDENTIALS:
+ log.debug("Rejecting request: missing credentials");
+ sendErrorResponse(response, HttpStatus.UNAUTHORIZED,
+ "Authentication required. Please provide hawkBit credentials.");
+ break;
+
+ case INVALID_CREDENTIALS:
+ log.debug("Rejecting request: invalid credentials");
+ request.removeAttribute(AUTH_HEADER_ATTRIBUTE);
+ sendErrorResponse(response, HttpStatus.UNAUTHORIZED,
+ "Invalid hawkBit credentials.");
+ break;
+
+ case HAWKBIT_ERROR:
+ log.warn("Rejecting request: hawkBit unavailable");
+ request.removeAttribute(AUTH_HEADER_ATTRIBUTE);
+ sendErrorResponse(response, HttpStatus.SERVICE_UNAVAILABLE,
+ "Unable to validate credentials. hawkBit may be unavailable.");
+ break;
+ }
+ }
+
+ private void sendErrorResponse(final HttpServletResponse response, final HttpStatus status, final String message)
+ throws IOException {
+ response.setStatus(status.value());
+ response.setContentType(MediaType.APPLICATION_JSON_VALUE);
+ response.getWriter().write(String.format(
+ "{\"error\":\"%s\",\"message\":\"%s\"}",
+ status.getReasonPhrase(),
+ message));
+ }
+ }
+}
diff --git a/hawkbit-mcp/src/main/java/org/eclipse/hawkbit/mcp/server/config/McpToolConfiguration.java b/hawkbit-mcp/src/main/java/org/eclipse/hawkbit/mcp/server/config/McpToolConfiguration.java
new file mode 100644
index 0000000000..6320303e22
--- /dev/null
+++ b/hawkbit-mcp/src/main/java/org/eclipse/hawkbit/mcp/server/config/McpToolConfiguration.java
@@ -0,0 +1,83 @@
+/**
+ * Copyright (c) 2026 Contributors to the Eclipse Foundation
+ *
+ * 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.server.config;
+
+import org.eclipse.hawkbit.mcp.server.prompts.HawkbitPromptProvider;
+import org.eclipse.hawkbit.mcp.server.resources.HawkbitDocumentationResource;
+import org.eclipse.hawkbit.mcp.server.tools.HawkbitMcpToolProvider;
+import org.eclipse.hawkbit.sdk.HawkbitClient;
+import org.eclipse.hawkbit.sdk.Tenant;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * Configuration for MCP tools, resources, and prompts.
+ *
+ *
+ *
All beans use {@code @ConditionalOnMissingBean} - override by defining your own bean
Spring AI MCP auto-discovers {@code @Tool}, {@code @McpResource}, and {@code @McpPrompt} annotations
+ *
+ *
+ */
+@Configuration
+public class McpToolConfiguration {
+
+ /**
+ * Creates the hawkBit tool provider.
+ *
+ * Spring AI MCP auto-discovers {@code @McpTool} annotated methods on this bean.
+ * Override by defining your own {@code HawkBitMcpToolProvider} bean.
+ * Disable by setting {@code hawkbit.mcp.tools-enabled=false}.
+ * Individual operations can be enabled/disabled via {@code hawkbit.mcp.operations.*} properties.
+ *
+ */
+ @Bean
+ @ConditionalOnMissingBean
+ @ConditionalOnProperty(name = "hawkbit.mcp.tools-enabled", havingValue = "true", matchIfMissing = true)
+ public HawkbitMcpToolProvider hawkBitMcpToolProvider(
+ final HawkbitClient hawkbitClient,
+ final Tenant dummyTenant,
+ final HawkbitMcpProperties properties) {
+ return new HawkbitMcpToolProvider(hawkbitClient, dummyTenant, properties);
+ }
+
+ /**
+ * Creates the hawkBit documentation resource provider.
+ *
+ * Spring AI MCP auto-discovers {@code @McpResource} annotated methods on this bean.
+ * Override by defining your own {@code HawkBitDocumentationResource} bean.
+ * Disable by setting {@code hawkbit.mcp.resources-enabled=false}.
+ *
+ * Spring AI MCP auto-discovers {@code @McpPrompt} annotated methods on this bean.
+ * Override by defining your own {@code HawkBitPromptProvider} bean.
+ * Disable by setting {@code hawkbit.mcp.prompts-enabled=false}.
+ *
+ */
+ @Bean
+ @ConditionalOnMissingBean
+ @ConditionalOnProperty(name = "hawkbit.mcp.prompts-enabled", havingValue = "true", matchIfMissing = true)
+ public HawkbitPromptProvider hawkBitPromptProvider() {
+ return new HawkbitPromptProvider();
+ }
+}
diff --git a/hawkbit-mcp/src/main/java/org/eclipse/hawkbit/mcp/server/dto/ActionOperation.java b/hawkbit-mcp/src/main/java/org/eclipse/hawkbit/mcp/server/dto/ActionOperation.java
new file mode 100644
index 0000000000..43aa212d1d
--- /dev/null
+++ b/hawkbit-mcp/src/main/java/org/eclipse/hawkbit/mcp/server/dto/ActionOperation.java
@@ -0,0 +1,18 @@
+/**
+ * Copyright (c) 2026 Contributors to the Eclipse Foundation
+ *
+ * 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.server.dto;
+
+/**
+ * Operations for action management (actions are created indirectly via DS assignment).
+ */
+public enum ActionOperation {
+ DELETE,
+ DELETE_BATCH
+}
diff --git a/hawkbit-mcp/src/main/java/org/eclipse/hawkbit/mcp/server/dto/ListRequest.java b/hawkbit-mcp/src/main/java/org/eclipse/hawkbit/mcp/server/dto/ListRequest.java
new file mode 100644
index 0000000000..bc425a503f
--- /dev/null
+++ b/hawkbit-mcp/src/main/java/org/eclipse/hawkbit/mcp/server/dto/ListRequest.java
@@ -0,0 +1,42 @@
+/**
+ * Copyright (c) 2026 Contributors to the Eclipse Foundation
+ *
+ * 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.server.dto;
+
+import com.fasterxml.jackson.annotation.JsonPropertyDescription;
+
+/**
+ * Common request parameters for list operations.
+ */
+public record ListRequest(
+ @JsonPropertyDescription("RSQL filter query (e.g., 'name==test*')")
+ String rsql,
+
+ @JsonPropertyDescription("Number of items to skip (default: 0)")
+ Integer offset,
+
+ @JsonPropertyDescription("Maximum number of items to return (default: 50)")
+ Integer limit
+) {
+
+ public static final int DEFAULT_OFFSET = 0;
+ public static final int DEFAULT_LIMIT = 50;
+
+ public int getOffsetOrDefault() {
+ return offset != null ? offset : DEFAULT_OFFSET;
+ }
+
+ public int getLimitOrDefault() {
+ return limit != null ? limit : DEFAULT_LIMIT;
+ }
+
+ public String getRsqlOrNull() {
+ return rsql != null && !rsql.isBlank() ? rsql : null;
+ }
+}
diff --git a/hawkbit-mcp/src/main/java/org/eclipse/hawkbit/mcp/server/dto/ManageActionRequest.java b/hawkbit-mcp/src/main/java/org/eclipse/hawkbit/mcp/server/dto/ManageActionRequest.java
new file mode 100644
index 0000000000..c22738e3d8
--- /dev/null
+++ b/hawkbit-mcp/src/main/java/org/eclipse/hawkbit/mcp/server/dto/ManageActionRequest.java
@@ -0,0 +1,28 @@
+/**
+ * Copyright (c) 2026 Contributors to the Eclipse Foundation
+ *
+ * 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.server.dto;
+
+import java.util.List;
+
+/**
+ * Request wrapper for action management operations.
+ * Actions are created indirectly via distribution set assignment, so only DELETE operations are supported.
+ *
+ * @param operation the operation to perform (DELETE, DELETE_BATCH)
+ * @param actionId the action ID (for DELETE single action)
+ * @param actionIds list of action IDs (for DELETE_BATCH)
+ * @param rsql RSQL filter query (alternative for DELETE_BATCH)
+ */
+public record ManageActionRequest(
+ ActionOperation operation,
+ Long actionId,
+ List actionIds,
+ String rsql
+) {}
diff --git a/hawkbit-mcp/src/main/java/org/eclipse/hawkbit/mcp/server/dto/ManageDistributionSetRequest.java b/hawkbit-mcp/src/main/java/org/eclipse/hawkbit/mcp/server/dto/ManageDistributionSetRequest.java
new file mode 100644
index 0000000000..f6b305a49f
--- /dev/null
+++ b/hawkbit-mcp/src/main/java/org/eclipse/hawkbit/mcp/server/dto/ManageDistributionSetRequest.java
@@ -0,0 +1,30 @@
+/**
+ * Copyright (c) 2026 Contributors to the Eclipse Foundation
+ *
+ * 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.server.dto;
+
+import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtDistributionSetRequestBodyPost;
+import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtDistributionSetRequestBodyPut;
+
+/**
+ * Request wrapper for distribution set management operations.
+ * Reuses existing {@link MgmtDistributionSetRequestBodyPost} for CREATE and
+ * {@link MgmtDistributionSetRequestBodyPut} for UPDATE.
+ *
+ * @param operation the operation to perform (CREATE, UPDATE, DELETE)
+ * @param distributionSetId the distribution set ID (required for UPDATE/DELETE)
+ * @param createBody the request body for CREATE operation
+ * @param updateBody the request body for UPDATE operation
+ */
+public record ManageDistributionSetRequest(
+ Operation operation,
+ Long distributionSetId,
+ MgmtDistributionSetRequestBodyPost createBody,
+ MgmtDistributionSetRequestBodyPut updateBody
+) {}
diff --git a/hawkbit-mcp/src/main/java/org/eclipse/hawkbit/mcp/server/dto/ManageRolloutRequest.java b/hawkbit-mcp/src/main/java/org/eclipse/hawkbit/mcp/server/dto/ManageRolloutRequest.java
new file mode 100644
index 0000000000..fe7d546851
--- /dev/null
+++ b/hawkbit-mcp/src/main/java/org/eclipse/hawkbit/mcp/server/dto/ManageRolloutRequest.java
@@ -0,0 +1,32 @@
+/**
+ * Copyright (c) 2026 Contributors to the Eclipse Foundation
+ *
+ * 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.server.dto;
+
+import org.eclipse.hawkbit.mgmt.json.model.rollout.MgmtRolloutRestRequestBodyPost;
+import org.eclipse.hawkbit.mgmt.json.model.rollout.MgmtRolloutRestRequestBodyPut;
+
+/**
+ * Request wrapper for rollout management operations including CRUD and lifecycle.
+ * Reuses existing {@link MgmtRolloutRestRequestBodyPost} for CREATE and
+ * {@link MgmtRolloutRestRequestBodyPut} for UPDATE.
+ *
+ * @param operation the operation to perform (CREATE, UPDATE, DELETE, START, PAUSE, STOP, RESUME, APPROVE, DENY, RETRY, TRIGGER_NEXT_GROUP)
+ * @param rolloutId the rollout ID (required for UPDATE/DELETE/lifecycle operations)
+ * @param createBody the request body for CREATE operation
+ * @param updateBody the request body for UPDATE operation
+ * @param remark optional remark for APPROVE/DENY operations
+ */
+public record ManageRolloutRequest(
+ RolloutOperation operation,
+ Long rolloutId,
+ MgmtRolloutRestRequestBodyPost createBody,
+ MgmtRolloutRestRequestBodyPut updateBody,
+ String remark
+) {}
diff --git a/hawkbit-mcp/src/main/java/org/eclipse/hawkbit/mcp/server/dto/ManageSoftwareModuleRequest.java b/hawkbit-mcp/src/main/java/org/eclipse/hawkbit/mcp/server/dto/ManageSoftwareModuleRequest.java
new file mode 100644
index 0000000000..1790ae1834
--- /dev/null
+++ b/hawkbit-mcp/src/main/java/org/eclipse/hawkbit/mcp/server/dto/ManageSoftwareModuleRequest.java
@@ -0,0 +1,30 @@
+/**
+ * Copyright (c) 2026 Contributors to the Eclipse Foundation
+ *
+ * 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.server.dto;
+
+import org.eclipse.hawkbit.mgmt.json.model.softwaremodule.MgmtSoftwareModuleRequestBodyPost;
+import org.eclipse.hawkbit.mgmt.json.model.softwaremodule.MgmtSoftwareModuleRequestBodyPut;
+
+/**
+ * Request wrapper for software module management operations.
+ * Reuses existing {@link MgmtSoftwareModuleRequestBodyPost} for CREATE and
+ * {@link MgmtSoftwareModuleRequestBodyPut} for UPDATE.
+ *
+ * @param operation the operation to perform (CREATE, UPDATE, DELETE)
+ * @param softwareModuleId the software module ID (required for UPDATE/DELETE)
+ * @param createBody the request body for CREATE operation
+ * @param updateBody the request body for UPDATE operation
+ */
+public record ManageSoftwareModuleRequest(
+ Operation operation,
+ Long softwareModuleId,
+ MgmtSoftwareModuleRequestBodyPost createBody,
+ MgmtSoftwareModuleRequestBodyPut updateBody
+) {}
diff --git a/hawkbit-mcp/src/main/java/org/eclipse/hawkbit/mcp/server/dto/ManageTargetFilterRequest.java b/hawkbit-mcp/src/main/java/org/eclipse/hawkbit/mcp/server/dto/ManageTargetFilterRequest.java
new file mode 100644
index 0000000000..223ba63208
--- /dev/null
+++ b/hawkbit-mcp/src/main/java/org/eclipse/hawkbit/mcp/server/dto/ManageTargetFilterRequest.java
@@ -0,0 +1,26 @@
+/**
+ * Copyright (c) 2026 Contributors to the Eclipse Foundation
+ *
+ * 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.server.dto;
+
+import org.eclipse.hawkbit.mgmt.json.model.targetfilter.MgmtTargetFilterQueryRequestBody;
+
+/**
+ * Request wrapper for target filter query management operations.
+ * Reuses existing {@link MgmtTargetFilterQueryRequestBody} for CREATE/UPDATE data.
+ *
+ * @param operation the operation to perform (CREATE, UPDATE, DELETE)
+ * @param filterId the target filter query ID (required for UPDATE/DELETE)
+ * @param body the request body containing filter data (for CREATE/UPDATE)
+ */
+public record ManageTargetFilterRequest(
+ Operation operation,
+ Long filterId,
+ MgmtTargetFilterQueryRequestBody body
+) {}
diff --git a/hawkbit-mcp/src/main/java/org/eclipse/hawkbit/mcp/server/dto/ManageTargetRequest.java b/hawkbit-mcp/src/main/java/org/eclipse/hawkbit/mcp/server/dto/ManageTargetRequest.java
new file mode 100644
index 0000000000..69faf57799
--- /dev/null
+++ b/hawkbit-mcp/src/main/java/org/eclipse/hawkbit/mcp/server/dto/ManageTargetRequest.java
@@ -0,0 +1,26 @@
+/**
+ * Copyright (c) 2026 Contributors to the Eclipse Foundation
+ *
+ * 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.server.dto;
+
+import org.eclipse.hawkbit.mgmt.json.model.target.MgmtTargetRequestBody;
+
+/**
+ * Request wrapper for target management operations.
+ * Reuses existing {@link MgmtTargetRequestBody} for CREATE/UPDATE data.
+ *
+ * @param operation the operation to perform (CREATE, UPDATE, DELETE)
+ * @param controllerId the target controller ID (required for UPDATE/DELETE, used as identifier for CREATE)
+ * @param body the request body containing target data (for CREATE/UPDATE)
+ */
+public record ManageTargetRequest(
+ Operation operation,
+ String controllerId,
+ MgmtTargetRequestBody body
+) {}
diff --git a/hawkbit-mcp/src/main/java/org/eclipse/hawkbit/mcp/server/dto/Operation.java b/hawkbit-mcp/src/main/java/org/eclipse/hawkbit/mcp/server/dto/Operation.java
new file mode 100644
index 0000000000..8edac82184
--- /dev/null
+++ b/hawkbit-mcp/src/main/java/org/eclipse/hawkbit/mcp/server/dto/Operation.java
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) 2026 Contributors to the Eclipse Foundation
+ *
+ * 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.server.dto;
+
+/**
+ * Standard CRUD operations for entity management tools.
+ */
+public enum Operation {
+ CREATE,
+ UPDATE,
+ DELETE
+}
diff --git a/hawkbit-mcp/src/main/java/org/eclipse/hawkbit/mcp/server/dto/OperationResponse.java b/hawkbit-mcp/src/main/java/org/eclipse/hawkbit/mcp/server/dto/OperationResponse.java
new file mode 100644
index 0000000000..40a056e28b
--- /dev/null
+++ b/hawkbit-mcp/src/main/java/org/eclipse/hawkbit/mcp/server/dto/OperationResponse.java
@@ -0,0 +1,55 @@
+/**
+ * Copyright (c) 2026 Contributors to the Eclipse Foundation
+ *
+ * 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.server.dto;
+
+/**
+ * Unified response wrapper for management operations.
+ *
+ * @param the type of the data payload
+ * @param operation the operation that was performed
+ * @param success whether the operation was successful
+ * @param message optional message (typically for success confirmations or error details)
+ * @param data the operation result data (e.g., created/updated entity)
+ */
+public record OperationResponse(
+ String operation,
+ boolean success,
+ String message,
+ T data
+) {
+
+ /**
+ * Creates a successful response with data.
+ */
+ public static OperationResponse success(final String operation, final T data) {
+ return new OperationResponse<>(operation, true, null, data);
+ }
+
+ /**
+ * Creates a successful response with a message (no data).
+ */
+ public static OperationResponse success(final String operation, final String message) {
+ return new OperationResponse<>(operation, true, message, null);
+ }
+
+ /**
+ * Creates a successful response with both message and data.
+ */
+ public static OperationResponse success(final String operation, final String message, final T data) {
+ return new OperationResponse<>(operation, true, message, data);
+ }
+
+ /**
+ * Creates a failure response with an error message.
+ */
+ public static OperationResponse failure(final String operation, final String message) {
+ return new OperationResponse<>(operation, false, message, null);
+ }
+}
diff --git a/hawkbit-mcp/src/main/java/org/eclipse/hawkbit/mcp/server/dto/PagedResponse.java b/hawkbit-mcp/src/main/java/org/eclipse/hawkbit/mcp/server/dto/PagedResponse.java
new file mode 100644
index 0000000000..c26107ddce
--- /dev/null
+++ b/hawkbit-mcp/src/main/java/org/eclipse/hawkbit/mcp/server/dto/PagedResponse.java
@@ -0,0 +1,29 @@
+/**
+ * Copyright (c) 2026 Contributors to the Eclipse Foundation
+ *
+ * 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.server.dto;
+
+import java.util.List;
+
+/**
+ * Generic paged response for MCP tool results.
+ *
+ * @param the type of items in the response
+ */
+public record PagedResponse(
+ List content,
+ long total,
+ int offset,
+ int limit
+) {
+
+ public static PagedResponse of(final List content, final long total, final int offset, final int limit) {
+ return new PagedResponse<>(content, total, offset, limit);
+ }
+}
diff --git a/hawkbit-mcp/src/main/java/org/eclipse/hawkbit/mcp/server/dto/RolloutOperation.java b/hawkbit-mcp/src/main/java/org/eclipse/hawkbit/mcp/server/dto/RolloutOperation.java
new file mode 100644
index 0000000000..994166230b
--- /dev/null
+++ b/hawkbit-mcp/src/main/java/org/eclipse/hawkbit/mcp/server/dto/RolloutOperation.java
@@ -0,0 +1,27 @@
+/**
+ * Copyright (c) 2026 Contributors to the Eclipse Foundation
+ *
+ * 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.server.dto;
+
+/**
+ * Operations for rollout management including CRUD and lifecycle operations.
+ */
+public enum RolloutOperation {
+ CREATE,
+ UPDATE,
+ DELETE,
+ START,
+ PAUSE,
+ STOP,
+ RESUME,
+ APPROVE,
+ DENY,
+ RETRY,
+ TRIGGER_NEXT_GROUP
+}
diff --git a/hawkbit-mcp/src/main/java/org/eclipse/hawkbit/mcp/server/prompts/HawkbitPromptProvider.java b/hawkbit-mcp/src/main/java/org/eclipse/hawkbit/mcp/server/prompts/HawkbitPromptProvider.java
new file mode 100644
index 0000000000..a41176c2ec
--- /dev/null
+++ b/hawkbit-mcp/src/main/java/org/eclipse/hawkbit/mcp/server/prompts/HawkbitPromptProvider.java
@@ -0,0 +1,67 @@
+/**
+ * Copyright (c) 2026 Contributors to the Eclipse Foundation
+ *
+ * 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.server.prompts;
+
+import io.modelcontextprotocol.spec.McpSchema.GetPromptResult;
+import io.modelcontextprotocol.spec.McpSchema.PromptMessage;
+import io.modelcontextprotocol.spec.McpSchema.Role;
+import io.modelcontextprotocol.spec.McpSchema.TextContent;
+import lombok.extern.slf4j.Slf4j;
+import org.springaicommunity.mcp.annotation.McpPrompt;
+import org.springframework.core.io.ClassPathResource;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+
+/**
+ * MCP prompts for hawkBit that provide initial context to LLMs.
+ *
+ * These prompts help LLMs understand what hawkBit is and what documentation
+ * resources are available at the start of a session.
+ *
+ */
+@Slf4j
+public class HawkbitPromptProvider {
+
+ private static final String PROMPTS_PATH = "prompts/";
+
+ @McpPrompt(
+ name = "hawkbit-context",
+ description = "Provides initial context about hawkBit, available tools, and documentation resources. " +
+ "Use this prompt at the start of a session to understand what you can do with hawkBit MCP.")
+ public GetPromptResult getHawkBitContext() {
+ return new GetPromptResult(
+ "hawkBit MCP Server Context",
+ List.of(new PromptMessage(Role.ASSISTANT, new TextContent(loadPrompt("hawkbit-context.md"))))
+ );
+ }
+
+ @McpPrompt(
+ name = "rsql-help",
+ description = "Explains RSQL query syntax for filtering hawkBit entities. " +
+ "Use this when you need help constructing filter queries for targets, rollouts, etc.")
+ public GetPromptResult getRsqlHelp() {
+ return new GetPromptResult(
+ "RSQL Query Help",
+ List.of(new PromptMessage(Role.ASSISTANT, new TextContent(loadPrompt("rsql-help.md"))))
+ );
+ }
+
+ private String loadPrompt(final String filename) {
+ try {
+ ClassPathResource resource = new ClassPathResource(PROMPTS_PATH + filename);
+ return resource.getContentAsString(StandardCharsets.UTF_8);
+ } catch (IOException e) {
+ log.error("Failed to load prompt: {}", filename, e);
+ return "Prompt content not available.";
+ }
+ }
+}
diff --git a/hawkbit-mcp/src/main/java/org/eclipse/hawkbit/mcp/server/resources/HawkbitDocumentationResource.java b/hawkbit-mcp/src/main/java/org/eclipse/hawkbit/mcp/server/resources/HawkbitDocumentationResource.java
new file mode 100644
index 0000000000..e53b1f5030
--- /dev/null
+++ b/hawkbit-mcp/src/main/java/org/eclipse/hawkbit/mcp/server/resources/HawkbitDocumentationResource.java
@@ -0,0 +1,201 @@
+/**
+ * Copyright (c) 2026 Contributors to the Eclipse Foundation
+ *
+ * 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.server.resources;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springaicommunity.mcp.annotation.McpResource;
+import org.springframework.core.io.ClassPathResource;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+
+/**
+ * MCP resources providing hawkBit documentation for LLMs.
+ */
+@Slf4j
+public class HawkbitDocumentationResource {
+
+ private static final String DOCS_PATH = "hawkbit-docs/";
+
+ @McpResource(
+ uri = "hawkbit://docs/overview",
+ name = "hawkBit Overview",
+ description = "High-level introduction to hawkBit: interfaces (DDI, DMF, Management API), " +
+ "rollout management, and package model for IoT software updates")
+ public String getOverview() {
+ return loadDoc("README.md");
+ }
+
+ @McpResource(
+ uri = "hawkbit://docs/what-is-hawkbit",
+ name = "What is hawkBit",
+ description = "Explains what hawkBit is, why IoT software updates matter, " +
+ "and scalability features for cloud deployments")
+ public String getWhatIsHawkbit() {
+ return loadDoc("what-is-hawkbit.md");
+ }
+
+ @McpResource(
+ uri = "hawkbit://docs/quick-start",
+ name = "Quick Start Guide",
+ description = "Docker-based setup guides for monolith and microservices deployments, " +
+ "building from sources, and credential configuration")
+ public String getQuickStart() {
+ return loadDoc("quick-start.md");
+ }
+
+ @McpResource(
+ uri = "hawkbit://docs/features",
+ name = "Feature Overview",
+ description = "Comprehensive feature list: device repository, software management, " +
+ "artifact delivery, rollout management, and API interfaces")
+ public String getFeatures() {
+ return loadDoc("features.md");
+ }
+
+ @McpResource(
+ uri = "hawkbit://docs/architecture",
+ name = "System Architecture",
+ description = "Architecture overview with module diagram and third-party technology stack")
+ public String getArchitecture() {
+ return loadDoc("architecture.md");
+ }
+
+ @McpResource(
+ uri = "hawkbit://docs/base-setup",
+ name = "Production Setup",
+ description = "Configuring production infrastructure with MariaDB/MySQL database " +
+ "and RabbitMQ for DMF (Device Management Federation) communication")
+ public String getBaseSetup() {
+ return loadDoc("base-setup.md");
+ }
+
+ @McpResource(
+ uri = "hawkbit://docs/sdk",
+ name = "SDK Guide",
+ description = "hawkBit SDK for device and gateway integration: configuration properties, " +
+ "Maven dependencies, and usage examples with DdiTenant and MgmtAPI clients")
+ public String getSdkGuide() {
+ return loadDoc("hawkbit-sdk.md");
+ }
+
+ @McpResource(
+ uri = "hawkbit://docs/feign-client",
+ name = "Feign Client Guide",
+ description = "Creating Feign-based REST clients for Management API and DDI API " +
+ "with Spring Boot integration examples")
+ public String getFeignClientGuide() {
+ return loadDoc("feign-client.md");
+ }
+
+ @McpResource(
+ uri = "hawkbit://docs/clustering",
+ name = "Clustering Guide",
+ description = "Running hawkBit in clustered environments: Spring Cloud Stream event distribution, " +
+ "caching with TTL, scheduler behavior, and DoS filter constraints")
+ public String getClusteringGuide() {
+ return loadDoc("clustering.md");
+ }
+
+ @McpResource(
+ uri = "hawkbit://docs/authentication",
+ name = "Authentication",
+ description = "Security token authentication (target and gateway tokens), certificate-based auth " +
+ "via reverse proxy, TLS/mTLS setup, and Nginx configuration examples")
+ public String getAuthentication() {
+ return loadDoc("authentication.md");
+ }
+
+ @McpResource(
+ uri = "hawkbit://docs/authorization",
+ name = "Authorization",
+ description = "Fine-grained permission system for Management API/UI, DDI API authorization, " +
+ "permission groups, OpenID Connect support, and role-based access control")
+ public String getAuthorization() {
+ return loadDoc("authorization.md");
+ }
+
+ @McpResource(
+ uri = "hawkbit://docs/datamodel",
+ name = "Data Model",
+ description = "Entity definitions: provisioning targets, distribution sets, software modules, " +
+ "artifacts, entity relationships, and soft/hard delete strategies")
+ public String getDataModel() {
+ return loadDoc("datamodel.md");
+ }
+
+ @McpResource(
+ uri = "hawkbit://docs/rollout-management",
+ name = "Rollout Management",
+ description = "Rollout campaigns: cascading deployment groups, success/error thresholds, " +
+ "approval workflow, multi-assignments (beta), action weight prioritization, and state machines")
+ public String getRolloutManagement() {
+ return loadDoc("rollout-management.md");
+ }
+
+ @McpResource(
+ uri = "hawkbit://docs/target-state",
+ name = "Target State",
+ description = "Target state definitions (UNKNOWN, IN_SYNC, PENDING, ERROR, REGISTERED) " +
+ "and state transition diagrams")
+ public String getTargetState() {
+ return loadDoc("targetstate.md");
+ }
+
+ @McpResource(
+ uri = "hawkbit://docs/management-api",
+ name = "Management API",
+ description = "RESTful API for CRUD operations on targets and software: API versioning, " +
+ "HTTP methods, headers, error handling, and embedded Swagger UI reference")
+ public String getManagementApi() {
+ return loadDoc("management-api.md");
+ }
+
+ @McpResource(
+ uri = "hawkbit://docs/ddi-api",
+ name = "DDI API (Direct Device Integration)",
+ description = "HTTP polling-based device integration API: state machine mapping, " +
+ "status feedback mechanisms, update retrieval, and embedded Swagger UI reference")
+ public String getDdiApi() {
+ return loadDoc("direct-device-integration-api.md");
+ }
+
+ @McpResource(
+ uri = "hawkbit://docs/dmf-api",
+ name = "DMF API (Device Management Federation)",
+ description = "AMQP-based indirect device integration: message formats (THING_CREATED, etc.), " +
+ "exchanges, queues, bindings, and high-throughput service-to-service communication")
+ public String getDmfApi() {
+ return loadDoc("device-management-federation-api.md");
+ }
+
+ @McpResource(
+ uri = "hawkbit://docs/entity-definitions",
+ name = "hawkBit Entity Definitions",
+ description = "RSQL filtering syntax for querying targets, rollouts, distribution sets, " +
+ "actions, software modules, and target filter queries with examples")
+ public String getEntityDefinitions() {
+ return loadResource("hawkbit-entity-definitions.md");
+ }
+
+ private String loadDoc(final String filename) {
+ return loadResource(DOCS_PATH + filename);
+ }
+
+ private String loadResource(final String path) {
+ try {
+ ClassPathResource resource = new ClassPathResource(path);
+ return resource.getContentAsString(StandardCharsets.UTF_8);
+ } catch (IOException e) {
+ log.error("Failed to load documentation: {}", path, e);
+ return "Documentation not available. Please refer to the hawkBit documentation at https://eclipse.dev/hawkbit/";
+ }
+ }
+}
diff --git a/hawkbit-mcp/src/main/java/org/eclipse/hawkbit/mcp/server/tools/HawkbitMcpToolProvider.java b/hawkbit-mcp/src/main/java/org/eclipse/hawkbit/mcp/server/tools/HawkbitMcpToolProvider.java
new file mode 100644
index 0000000000..9b9c883b2e
--- /dev/null
+++ b/hawkbit-mcp/src/main/java/org/eclipse/hawkbit/mcp/server/tools/HawkbitMcpToolProvider.java
@@ -0,0 +1,553 @@
+/**
+ * Copyright (c) 2026 Contributors to the Eclipse Foundation
+ *
+ * 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.server.tools;
+
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.eclipse.hawkbit.mcp.server.config.HawkbitMcpProperties;
+import org.eclipse.hawkbit.mcp.server.dto.ActionOperation;
+import org.eclipse.hawkbit.mcp.server.dto.ListRequest;
+import org.eclipse.hawkbit.mcp.server.dto.ManageActionRequest;
+import org.eclipse.hawkbit.mcp.server.dto.ManageDistributionSetRequest;
+import org.eclipse.hawkbit.mcp.server.dto.ManageRolloutRequest;
+import org.eclipse.hawkbit.mcp.server.dto.ManageSoftwareModuleRequest;
+import org.eclipse.hawkbit.mcp.server.dto.ManageTargetFilterRequest;
+import org.eclipse.hawkbit.mcp.server.dto.ManageTargetRequest;
+import org.eclipse.hawkbit.mcp.server.dto.Operation;
+import org.eclipse.hawkbit.mcp.server.dto.OperationResponse;
+import org.eclipse.hawkbit.mcp.server.dto.PagedResponse;
+import org.eclipse.hawkbit.mcp.server.dto.RolloutOperation;
+import org.eclipse.hawkbit.mgmt.json.model.PagedList;
+import org.eclipse.hawkbit.mgmt.json.model.action.MgmtAction;
+import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtDistributionSet;
+import org.eclipse.hawkbit.mgmt.json.model.rollout.MgmtRolloutResponseBody;
+import org.eclipse.hawkbit.mgmt.json.model.softwaremodule.MgmtSoftwareModule;
+import org.eclipse.hawkbit.mgmt.json.model.target.MgmtTarget;
+import org.eclipse.hawkbit.mgmt.json.model.targetfilter.MgmtTargetFilterQuery;
+import org.eclipse.hawkbit.mgmt.rest.api.MgmtActionRestApi;
+import org.eclipse.hawkbit.mgmt.rest.api.MgmtDistributionSetRestApi;
+import org.eclipse.hawkbit.mgmt.rest.api.MgmtRolloutRestApi;
+import org.eclipse.hawkbit.mgmt.rest.api.MgmtSoftwareModuleRestApi;
+import org.eclipse.hawkbit.mgmt.rest.api.MgmtTargetFilterQueryRestApi;
+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.springframework.http.ResponseEntity;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * MCP tools for hawkBit using the SDK.
+ *
+ * Provides tools for managing targets, rollouts, distribution sets, actions,
+ * software modules, and target filter queries via the hawkBit REST API.
+ *
+ */
+@Slf4j
+@RequiredArgsConstructor
+public class HawkbitMcpToolProvider {
+
+ private static final String OP_CREATE = "CREATE";
+ private static final String OP_UPDATE = "UPDATE";
+ private static final String OP_DELETE = "DELETE";
+ private static final String OP_DELETE_BATCH = "DELETE_BATCH";
+ private static final String OP_START = "START";
+ private static final String OP_PAUSE = "PAUSE";
+ private static final String OP_STOP = "STOP";
+ private static final String OP_RESUME = "RESUME";
+ private static final String OP_APPROVE = "APPROVE";
+ private static final String OP_DENY = "DENY";
+ private static final String OP_RETRY = "RETRY";
+ private static final String OP_TRIGGER_NEXT_GROUP = "TRIGGER_NEXT_GROUP";
+
+ private final HawkbitClient hawkbitClient;
+ private final Tenant dummyTenant;
+ private final HawkbitMcpProperties properties;
+
+ private PagedResponse toPagedResponse(final PagedList pagedList, final ListRequest request) {
+ if (pagedList == null) {
+ return PagedResponse.of(
+ Collections.emptyList(),
+ 0L,
+ request.getOffsetOrDefault(),
+ request.getLimitOrDefault());
+ }
+ return PagedResponse.of(
+ pagedList.getContent(),
+ pagedList.getTotal(),
+ request.getOffsetOrDefault(),
+ request.getLimitOrDefault());
+ }
+
+ @McpTool(name = "list_targets",
+ description = "Retrieves a paged list of targets (devices) with optional RSQL filtering. " +
+ "Targets represent devices that can receive software updates.")
+ public PagedResponse listTargets(final ListRequest request) {
+ log.debug("Listing targets with rsql={}, offset={}, limit={}",
+ request.rsql(), request.getOffsetOrDefault(), request.getLimitOrDefault());
+
+ MgmtTargetRestApi targetApi = hawkbitClient.mgmtService(MgmtTargetRestApi.class, dummyTenant);
+ ResponseEntity> response = targetApi.getTargets(
+ request.getRsqlOrNull(),
+ request.getOffsetOrDefault(),
+ request.getLimitOrDefault(),
+ null);
+
+ return toPagedResponse(response.getBody(), request);
+ }
+
+ @McpTool(name = "list_rollouts",
+ description = "Retrieves a paged list of rollouts with optional RSQL filtering. " +
+ "Rollouts are used to deploy software to groups of targets.")
+ public PagedResponse listRollouts(final ListRequest request) {
+ log.debug("Listing rollouts with rsql={}, offset={}, limit={}",
+ request.rsql(), request.getOffsetOrDefault(), request.getLimitOrDefault());
+
+ MgmtRolloutRestApi rolloutApi = hawkbitClient.mgmtService(MgmtRolloutRestApi.class, dummyTenant);
+ ResponseEntity> response = rolloutApi.getRollouts(
+ request.getRsqlOrNull(),
+ request.getOffsetOrDefault(),
+ request.getLimitOrDefault(),
+ null,
+ null);
+
+ return toPagedResponse(response.getBody(), request);
+ }
+
+ @McpTool(name = "list_distribution_sets",
+ description = "Retrieves a paged list of distribution sets with optional RSQL filtering. " +
+ "Distribution sets are software packages that can be deployed to targets.")
+ public PagedResponse listDistributionSets(final ListRequest request) {
+ log.debug("Listing distribution sets with rsql={}, offset={}, limit={}",
+ request.rsql(), request.getOffsetOrDefault(), request.getLimitOrDefault());
+
+ MgmtDistributionSetRestApi dsApi = hawkbitClient.mgmtService(MgmtDistributionSetRestApi.class, dummyTenant);
+ ResponseEntity> response = dsApi.getDistributionSets(
+ request.getRsqlOrNull(),
+ request.getOffsetOrDefault(),
+ request.getLimitOrDefault(),
+ null);
+
+ return toPagedResponse(response.getBody(), request);
+ }
+
+ @McpTool(name = "list_actions",
+ description = "Retrieves a paged list of actions with optional RSQL filtering. " +
+ "Actions represent deployment operations assigned to targets.")
+ public PagedResponse listActions(final ListRequest request) {
+ log.debug("Listing actions with rsql={}, offset={}, limit={}",
+ request.rsql(), request.getOffsetOrDefault(), request.getLimitOrDefault());
+
+ MgmtActionRestApi actionApi = hawkbitClient.mgmtService(MgmtActionRestApi.class, dummyTenant);
+ ResponseEntity> response = actionApi.getActions(
+ request.getRsqlOrNull(),
+ request.getOffsetOrDefault(),
+ request.getLimitOrDefault(),
+ null,
+ null);
+
+ return toPagedResponse(response.getBody(), request);
+ }
+
+ @McpTool(name = "list_software_modules",
+ description = "Retrieves a paged list of software modules with optional RSQL filtering. " +
+ "Software modules are individual software components within distribution sets.")
+ public PagedResponse listSoftwareModules(final ListRequest request) {
+ log.debug("Listing software modules with rsql={}, offset={}, limit={}",
+ request.rsql(), request.getOffsetOrDefault(), request.getLimitOrDefault());
+
+ MgmtSoftwareModuleRestApi smApi = hawkbitClient.mgmtService(MgmtSoftwareModuleRestApi.class, dummyTenant);
+ ResponseEntity> response = smApi.getSoftwareModules(
+ request.getRsqlOrNull(),
+ request.getOffsetOrDefault(),
+ request.getLimitOrDefault(),
+ null);
+
+ return toPagedResponse(response.getBody(), request);
+ }
+
+ @McpTool(name = "list_target_filters",
+ description = "Retrieves a paged list of target filter queries with optional RSQL filtering. " +
+ "Target filters define RSQL queries to group targets for rollouts or auto-assignment.")
+ public PagedResponse listTargetFilters(final ListRequest request) {
+ log.debug("Listing target filters with rsql={}, offset={}, limit={}",
+ request.rsql(), request.getOffsetOrDefault(), request.getLimitOrDefault());
+
+ MgmtTargetFilterQueryRestApi filterApi = hawkbitClient.mgmtService(MgmtTargetFilterQueryRestApi.class, dummyTenant);
+ ResponseEntity> response = filterApi.getFilters(
+ request.getRsqlOrNull(),
+ request.getOffsetOrDefault(),
+ request.getLimitOrDefault(),
+ null,
+ null);
+
+ return toPagedResponse(response.getBody(), request);
+ }
+
+ @McpTool(name = "manage_target",
+ description = "Create, update, or delete targets (devices). " +
+ "Operations: CREATE (new target with controllerId, name, description), " +
+ "UPDATE (modify existing target by controllerId), " +
+ "DELETE (remove target by controllerId)")
+ public OperationResponse