Skip to main content

mta-descriptor

Invoke this skill to generate MTA deployment descriptor (mtad.yaml) for Cloud Foundry. Creates mtad.yaml with proper service bindings based on migration skills applied. Always invoke LAST after all other migration skills.

Source facts

Repository
SAP-samples/btp-neo-java-app-migration
Last source activity
July 29, 2026 at 12:47
Detected SKILL.md language
English
Stars
6
Forks
0

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

File Explorer
3 files

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
mta-descriptor
description
Invoke this skill to generate MTA deployment descriptor (mtad.yaml) for Cloud Foundry. Creates mtad.yaml with proper service bindings based on migration skills applied. Always invoke LAST after all other migration skills.
disable-model-invocation
false
allowed-tools
Read, Edit, Write, Bash, Grep, Glob
# MTA Descriptor Generation Generate a Multi-Target Application deployment descriptor (`mtad.yaml`) for deploying Neo-migrated applications to SAP BTP Cloud Foundry. ## Usage ``` Generate an mtad.yaml for my migrated application Create MTA descriptor with XSUAA and HANA services ``` ## Overview The MTA descriptor (`mtad.yaml`) defines: - Application modules (Java app, approuter) - Required Cloud Foundry services (XSUAA, HANA, Destination, etc.) - Service bindings between modules and services - Build and deployment parameters This skill analyzes which migration skills were applied to your application and generates the appropriate `mtad.yaml` configuration. ## Prerequisites > **Working directory:** This skill must run inside the `-cf-migration` copy of your app, created by `jakarta-java25-migration` or `neo-to-cf-migration-orchestrator`. If your current directory does not end in `-cf-migration`, switch to it before proceeding. Before invoking this skill, ensure you have invoked all required migration skills: 1. **jakarta-java25-migration** - `Use the jakarta-java25-migration skill` (REQUIRED) 2. **sdk-replacement** - `Use the sdk-replacement skill` (REQUIRED) 3. Any feature skills that apply to your application Also required: - Application successfully compiles: `mvn clean compile` - Understanding of which CF services your application requires ## Determine Required Services Based on the migration skills you've applied, identify required CF services: | Migration Skill Applied | Required CF Service | Service Type | |------------------------|---------------------|--------------| | authentication-xsuaa | XSUAA | `org.cloudfoundry.managed-service` | | destinations | Destination | `org.cloudfoundry.managed-service` | | connectivity-onpremise | Destination + Connectivity | `org.cloudfoundry.managed-service` | | persistence-hana | HANA Schema | `com.sap.xs.hana-schema` | | document-management-sdm | SDM | `org.cloudfoundry.managed-service` | | mail-destinations | (uses Destination service) | - | | keystore-credstore | Credential Store | `org.cloudfoundry.managed-service` | | monitoring-logging | (built-in CF logging) | - | | tomee-runtime | (requires TARGET_RUNTIME property) | - | ## MTA Templates ### Template Selection Choose the appropriate base template: | Scenario | Template | When to Use | |----------|----------|-------------| | Basic app without web authentication | [mtad-base.yaml](assets/mtad-base.yaml) | Internal services, background jobs, APIs with technical auth | | App with XSUAA and Approuter | [mtad-with-approuter.yaml](assets/mtad-with-approuter.yaml) | User-facing web applications requiring authentication | | TomEE application | Add `TARGET_RUNTIME: tomee` | Apps using EJB or requiring TomEE runtime | ### Precondition — `pom.xml` MUST configure `maven-war-plugin` with `<warName>${project.artifactId}</warName>` > **This skill owns the WAR filename rule (next section), which only holds > if `pom.xml` actually writes the WAR under `<artifactId>.war`.** Maven's > default WAR filename is `<artifactId>-<version>.war`, which does NOT > match what this skill writes into `mtad.yaml` (`target/<artifactId>.war`) > — the deploy then fails with: > > > `Error retrieving MTA: Error building MTA Archive:` > > `file path .../target/<artifactId>.war not found` > > The other migration skills (`sdk-replacement`, `tomee-runtime`, …) are > dependency-management skills and do not own build packaging. Verify and, > if needed, add the `<build>` block below to `pom.xml` **before** emitting > the descriptor. Required `<build>` block in `pom.xml`: ```xml <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-war-plugin</artifactId> <version>3.4.0</version> <configuration> <warName>${project.artifactId}</warName> <failOnMissingWebXml>false</failOnMissingWebXml> </configuration> </plugin> </plugins> </build> ``` > **Why `3.4.0` specifically:** the default `maven-war-plugin:2.2` is > incompatible with Java 25 and fails with `Cannot access defaults field > of Properties`. The Common Issues section below has the same fix. **Self-check before continuing** — run this from the project root; it must print exactly `1`: ```bash grep -A 10 'maven-war-plugin' pom.xml | grep -c '<warName>${project.artifactId}</warName>' ``` If the count is `0`, the plugin is missing or `<warName>` is set to something else (often the leftover `<warName>ROOT</warName>` from older templates). Fix `pom.xml` before emitting `mtad.yaml`; the build will otherwise succeed but the descriptor and the WAR will disagree at deploy time. If `pom.xml` legitimately needs a different `<warName>` (e.g. the project must serve at `/` and uses `<warName>ROOT</warName>`), update the descriptor's `path:` to match — the rule is "descriptor follows pom", not "pom follows descriptor". ### WAR filename rule — read it from `pom.xml`, write it literally The templates below show `path: target/<artifactId>.war` as a placeholder. **Before emitting `mtad.yaml`, open `pom.xml`, read the `<artifactId>` value, and substitute it literally** — for a project whose `<artifactId>` is `connectivity`, write `path: target/connectivity.war`. Why this matters in concrete terms: - `cf deploy` reads `path:` as a **literal filesystem path** under the descriptor's directory. It does NOT evaluate Maven property expressions, so a descriptor that says `path: target/${project.artifactId}.war` fails at deploy time with `Error building MTA Archive: file path target/${project.artifactId}.war not found`. - `maven-war-plugin` in the shipped pom templates is configured with `<warName>${project.artifactId}</warName>` — Maven writes a WAR named after the artifactId (e.g. `target/connectivity.war`). The descriptor must match what Maven actually writes, not a placeholder string. **Consequence to communicate downstream:** `sap_java_buildpack_jakarta` always serves the app at `/` (ROOT context). It additionally registers the app at `/<warName>/` only if `web.xml` has a servlet mapped to `url-pattern: /`. Integration tests and approuter destinations should use `/` — not `/<artifactId>/` — unless the app explicitly maps a default servlet. ### Template: Basic Application (mtad-base.yaml) For applications without user authentication: ```yaml _schema-version: "3.3" ID: my-app version: 1.0.0 parameters: parameters: enable-parallel-deployments: true modules: - name: my-app-backend type: java.tomcat path: target/<artifactId>.war # ← substitute the literal artifactId from pom.xml parameters: memory: 1024M disk-quota: 1024M buildpack: sap_java_buildpack_jakarta properties: # Pin SapMachine JRE 25 explicitly. Always set this — never rely on the buildpack's # implicit JRE choice, and keep maven.compiler.target in pom.xml at the same major # version. Use SAPMachineJRE (bundled, offline) — NOT SAPMachineJDK (heavyweight, # online-only). JBP_CONFIG_COMPONENTS: "jres: ['com.sap.xs.java.buildpack.jre.SAPMachineJRE']" JBP_CONFIG_SAP_MACHINE_JRE: "{ version: 25.+ }" TARGET_RUNTIME: tomcat build-parameters: builder: maven build-result: target/*.war provides: - name: backend-api properties: url: ${default-url} requires: # Add service bindings here based on needs - name: my-app-hana # If using persistence-hana - name: my-app-destination # If using destinations resources: # Add required services here ``` ### Template: Application with XSUAA (mtad-with-approuter.yaml) For user-facing web applications: ```yaml _schema-version: "3.2" ID: my-app version: 0.0.1 parameters: enable-parallel-deployments: true modules: # Approuter module for authentication - name: my-app-approuter type: nodejs path: approuter parameters: memory: 256M disk-quota: 256M routes: - route: '${protocol}://my-app.${default-domain}' protocol: http1 properties: XS_APP_LOG_LEVEL: debug TENANT_HOST_PATTERN: '(.*).cfapps.sap.hana.ondemand.com' CF_NODEJS_LOGGING_LEVEL: "info" requires: - name: my-app-xsuaa - name: my-app-java-app group: destinations properties: name: backend-app-destination url: '~{neo-app-url}' forwardAuthToken: true # Backend Java application - name: my-app-backend type: java.tomcat path: target/<artifactId>.war # ← substitute the literal artifactId from pom.xml parameters: memory: 1024M disk-quota: 1024M buildpack: sap_java_buildpack_jakarta properties: ENABLE_SECURITY_JAVA_API_V2: true # Pin SapMachine JRE 25 explicitly. Always set this — never rely on the buildpack's # implicit JRE choice, and keep maven.compiler.target in pom.xml at the same major # version. Use SAPMachineJRE (bundled, offline) — NOT SAPMachineJDK (heavyweight, # online-only). JBP_CONFIG_COMPONENTS: "jres: ['com.sap.xs.java.buildpack.jre.SAPMachineJRE']" JBP_CONFIG_SAP_MACHINE_JRE: "{ version: 25.+ }" TARGET_RUNTIME: tomcat SET_LOGGING_LEVEL: 'ROOT: INFO' provides: - name: my-app-java-app properties: neo-app-url: '${default-url}' requires: - name: my-app-xsuaa - name: my-app-destination resources: - name: my-app-xsuaa type: org.cloudfoundry.managed-service parameters: service: xsuaa service-plan: application path: ./xs-security.json - name: my-app-destination type: org.cloudfoundry.managed-service parameters: service: destination service-plan: lite ``` > **Critical:** The Java backend `path` must point to the actual WAR file Maven produces — read the `<artifactId>` from `pom.xml` and substitute it literally (e.g. `path: target/connectivity.war` for a project whose `artifactId` is `connectivity`). See the "WAR filename rule" callout above this section. Note: `sap_java_buildpack_jakarta` always serves the app at `/` — the WAR filename does not affect the runtime context path. > **Note on `type: nodejs` vs `type: approuter.nodejs`:** Use `type: nodejs` when deploying the approuter with your own `approuter/` directory and `package.json` (which is the standard pattern for migrated Neo applications). The type `approuter.nodejs` is for the "managed approuter" pattern which is used only in specific multi-tenant scenarios. ## Service Configuration Examples Add these resource definitions based on services your application needs: ### XSUAA (Authentication) ```yaml resources: - name: ${app-name}-xsuaa type: org.cloudfoundry.managed-service parameters: service: xsuaa service-plan: application path: ./xs-security.json # Created by authentication-xsuaa skill ``` **Module requires:** ```yaml requires: - name: ${app-name}-xsuaa ``` ### Destination Service ```yaml resources: - name: ${app-name}-destination type: org.cloudfoundry.managed-service parameters: service: destination service-plan: lite ``` **Module requires:** ```yaml requires: - name: ${app-name}-destination ``` ### Connectivity Service (On-Premise) ```yaml resources: - name: ${app-name}-connectivity type: org.cloudfoundry.managed-service parameters: service: connectivity service-plan: lite ``` **Module requires:** ```yaml requires: - name: ${app-name}-connectivity - name: ${app-name}-destination # Connectivity requires Destination ``` ### HANA Schema > **CRITICAL — do NOT add `service: hana` or `service-plan: hdi-shared` > under `type: com.sap.xs.hana-schema`.** > > ❌ Anti-pattern that breaks deploy: > ```yaml > - name: ${app-name}-hana > type: com.sap.xs.hana-schema > parameters: > service: hana # ← WRONG for hana-schema > service-plan: hdi-shared # ← WRONG for hana-schema > ``` > `service:` and `service-plan:` are keys for the GENERIC > `type: org.cloudfoundry.managed-service` (xsuaa, destination, credstore, > sdm, …). `com.sap.xs.hana-schema` is a dedicated MTA resource type that > already implies the broker, the service, and the plan. Setting them > explicitly causes the controller to reject the create-service call: > > > `CF-UnprocessableEntity(10008): Invalid service plan. ... Ensure that > > the service plan is visible in your current space ...` ```yaml resources: - name: ${app-name}-hana type: com.sap.xs.hana-schema ``` > **NO `parameters:` block on the hana-schema resource.** Specifically, do > NOT set `schema-name:`, `service:`, or `service-plan:` here. MTA's > `com.sap.xs.hana-schema` type does not support any of these at the > resource level — the deployer drops them with > `Parameter(s) "{<name>-hana=[schema-name]}" are not supported in the > specified scope, or referenced by any other entities. These parameters > are not processed and will be lost`. The deploy still completes, but > the warning is noisy and signals a malformed descriptor. The reference > scenarios use only the two-line form above. **Module requires:** ```yaml requires: - name: ${app-name}-hana ```
View on GitHub
This SKILL.md is very large, so SkillsMP previews the first section here. View on GitHub