$npx -y skills add forcedotcom/sf-skills --skill experience-ui-bundle-custom-app-generateMUST activate when the project contains a uiBundles/*/src/ directory and the task involves creating or configuring a Custom Application for hosting a UI bundle in Lightning Experience. Use this skill when creating a CustomApplication metadata record to surface the UI bundle in th
| 1 | # Custom Application for React UI Bundles |
| 2 | Create and configure a Salesforce Custom Application that hosts a React UI bundle in Lightning Experience. This skill generates the CustomApplication metadata so the app appears in the Lightning App Launcher and can be accessed by internal users. |
| 3 | |
| 4 | Custom Applications differ from Experience Sites: they don't need Networks, CustomSite, DigitalExperienceConfig, or DigitalExperienceBundle metadata. The Custom Application acts as a thin launcher entry that delegates rendering to the React UI bundle referenced by `uiBundle`. |
| 5 | |
| 6 | ## Required Properties |
| 7 | Resolve all properties before generating any metadata. Each has a fallback chain — work through each option in order until a value is found. |
| 8 | |
| 9 | | Property | Format | How to Resolve | |
| 10 | |----------|--------|----------------| |
| 11 | | **appName** | `lowercamelcase` (e.g., `myInternalApp`) | The UI bundle name from `uiBundles/<name>/` directory | |
| 12 | | **appNamespace** | String | `namespace` in `sfdx-project.json` → `sf data query -q "SELECT NamespacePrefix FROM Organization" --target-org ${usernameOrAlias}` → default `c` | |
| 13 | | **appLabel** | Human-readable string | User-provided, or derive from appName by converting camelCase to Title Case | |
| 14 | |
| 15 | The `appNamespace` and `appName` connect the Custom Application to the correct React UI bundle. In newer API versions this uses `<uiBundle>{appNamespace}__{appName}</uiBundle>`; in older versions it uses `<webApplication>{appName}</webApplication>`. Getting this wrong means the app launcher entry exists but shows a blank page. Step 2 of the workflow determines which field to use. |
| 16 | |
| 17 | ## Generation Workflow |
| 18 | ### Step 1: Resolve All Required Properties |
| 19 | Determine values for all properties before constructing anything. Use the resolution strategies in the table above. |
| 20 | |
| 21 | ### Step 2: Query API Context (Version-Aware Field Discovery) |
| 22 | Call `salesforce-api-context` MCP tools to discover which fields exist for the target org's API version. This ensures the generated metadata is compatible with the user's Salesforce version. |
| 23 | |
| 24 | **Required calls:** |
| 25 | 1. Call `get_metadata_type_fields` for `CustomApplication` — check whether the `uiBundle` field exists |
| 26 | 2. Call `get_metadata_type_fields` for `UIBundle` — check whether the `target` field exists |
| 27 | |
| 28 | **Field resolution based on API response:** |
| 29 | |
| 30 | | Field Check | If present | If absent (older API version) | |
| 31 | |-------------|-----------|-------------------------------| |
| 32 | | `CustomApplication.uiBundle` | Use `<uiBundle>{appNamespace}__{appName}</uiBundle>` | Use `<webApplication>{appName}</webApplication>` (no namespace) | |
| 33 | | `UIBundle.target` | Use `<target>CustomApplication</target>` | Omit the `<target>` element entirely | |
| 34 | |
| 35 | If `salesforce-api-context` is unavailable after a real attempt, fall back to the newer field names (`uiBundle` + `target`). |
| 36 | |
| 37 | ### Step 3: Create the Project Structure |
| 38 | Create any files and directories that don't already exist: |
| 39 | |
| 40 | | Metadata Type | Path | |
| 41 | |--------------|------| |
| 42 | | CustomApplication | `<sourceDir>/applications/{appName}.app-meta.xml` | |
| 43 | |
| 44 | **Note:** `<sourceDir>` is determined from `sfdx-project.json`. Read `packageDirectories[]` and use the entry where `"default": true`; the full source directory is `<path>/main/default`. If no default is set, use the first entry. Commonly `force-app/main/default`, but this path is configurable. |
| 45 | |
| 46 | ### Step 4: Populate All Metadata Fields |
| 47 | Use the default template in the doc below. Values in `{braces}` are resolved property references — substitute them with the actual values from Step 1. Apply the field resolution from Step 2 to determine which XML elements to use. |
| 48 | |
| 49 | | Metadata Type | Template Reference | |
| 50 | |--------------|-------------------| |
| 51 | | CustomApplication | [configure-metadata-custom-application.md](references/configure-metadata-custom-application.md) | |
| 52 | |
| 53 | ### Execution Note for Step 4: Load and use the doc |
| 54 | - Agents MUST read the full contents of the references/*.md file referenced in Step 4 before attempting to populate metadata fields. |
| 55 | - Read the file in full, replace placeholders (e.g. `{appName}`) with the resolved values, then use the expanded template to populate the metadata XML content. |
| 56 | - If Step 2 determined the older field names apply, substitute `<uiBundle>` with `<webApplication>` in the generated output. |
| 57 | |
| 58 | ### Step 5: U |