Install
$ agentstack add skill-salesforcecommercecloud-b2c-developer-tooling-b2c-business-manager-extensions ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
Business Manager Extensions Skill
This skill guides you through creating Business Manager (BM) extension cartridges to customize the admin interface.
Overview
BM extensions add custom functionality to Business Manager through a single bm_extensions.xml descriptor plus a controller that renders ISML pages:
| Extension Type | Purpose | |----------------|---------| | Menu Items | Add a navigation section (a container in the Admin or a site's menu) | | Menu Actions | Add a functional link/page under a menu item — wired to a controller node | | Dialog Actions | Add buttons to existing BM pages at defined extension points | | Form Extensions | Add fields to existing BM search forms (order/customer search) |
⚠️ Schema essentials (read first)
These are the most common mistakes. The descriptor uses the bmmodules/2007-12-11 schema (bmext.xsd), not the extensibility/* namespace.
- Namespace is
http://www.demandware.com/xml/bmmodules/2007-12-11. name,short_description, anddescriptionare child elements with a requiredxml:langattribute — NOT attributes and NOT resource keys. Localize by repeating the element per language. There is noname="..."attribute.- `
is a plain string **element** holding a path (icons/q.png), NOT`. positionis ascending: lower numbers appear higher in the menu. Core BM items run 2000–12000, so custom items typically use a large number (e.g.99999) to sit at the bottom, or-1to float a "Home" action to the top.- The dialog-action extension-point attribute is
xp_ref(underscore), and dialog actions link to a menu action via a child `element — there is nomenuaction-ref` attribute. - Use the file encoding
ISO-8859-1to match platform samples.
To confirm any detail against the authoritative schema:
b2c docs schema bmext
File Structure
/bm_my_extension
/cartridge
bm_extensions.xml # Extension definitions (required)
bm_my_extension.properties # Cartridge properties (required)
/controllers
MyExtension.js # Controller for menu actions
/templates
/default
/extensions
mypage.isml # Custom BM pages
/static
/default
/icons
my-icon.png # Menu icons
bm_my_extension.properties is a standard cartridge marker file:
## cartridge.properties for cartridge bm_my_extension
demandware.cartridges.bm_my_extension.multipleLanguageStorefront=true
demandware.cartridges.bm_my_extension.id=bm_my_extension
Basic bm_extensions.xml
ACME Tools
ACME admin tools
Tools and utilities from the ACME team
icons/acme.png
Home
ACME Home
icons/acme.png
pipeline="ACME" resolves to the controller ACME.js; node="Start" resolves to its exported Start function (which must be marked Start.public = true).
Menu Items
Top-level navigation containers. site="false" places the item in the Administration menu; site="true" (the default) places it in a site menu.
ACME Tools
ACME Werkzeuge
ACME admin tools
icons/acme.png
| Attribute | Required | Default | Description | |-----------|----------|---------|-------------| | id | Yes | - | Unique identifier. Prefix to avoid collisions with core BM IDs. | | site | No | true | true = site menu, false = Administration menu | | position | No | - | Sort order — lower = higher. Use a large value to sit at the bottom. | | type | No | BM | BM or CSC (Customer Service Center) |
Child elements: name (required, localized), short_description, description (localized), icon (string path), sldsIconName (an SLDS utility icon name, preferred in the modern BM UI), exec.
Menu Actions
Functional pages under a menu item. Wire each to a controller node via ` and register every controller node it uses in `.
Product Export
Export products to CSV
icons/export.png
| Attribute | Required | Default | Description | |-----------|----------|---------|-------------| | id | Yes | - | Unique identifier | | menupath | No | - | Parent menu item id (or a core BM menu/item id to attach to it) | | site | No | true | Must match the parent menu item | | position | No | - | Sort order (lower = higher; -1 floats to top) | | extern | No | false | true opens the action in a new window |
Key child elements: name (required), short_description, description, extended_description, exec, sub-pipelines, parameters, icon.
> Note: ` takes only pipeline and node (both required) — there is no https attribute. The apis, scapis, and required-features` elements in the schema are not supported for custom menu actions.
The NoPermissionCheck idiom
BM checks that the logged-in user has permission for the menu action whose sub-pipelines register the requested controller node. For endpoints that must work without a module permission check — keepalive pings, service workers, polling/AJAX endpoints called by your page's JavaScript — register their nodes under a dedicated action with the reserved id NoPermissionCheck and no ``:
Dummy
Holds pipelines that do not require a permission check
Without this, those nodes 403 for users who haven't been granted the module. Only register nodes here that are genuinely safe to expose to any authenticated BM user.
Dialog Actions
Add buttons to existing BM pages at a named extension point (xp_ref).
Sync Order
| Attribute | Required | Default | Description | |-----------|----------|---------|-------------| | id | Yes | - | Unique identifier | | xp_ref | Yes | - | Extension point ID (note the underscore) | | position | No | - | Sort order | | class | No | - | Optional CSS class | | extern | No | false | true opens in a new window |
Child elements: name (optional, localized), exec (required), parameters, menuactions (one or more ` linking to the menu action that owns the permission check). Dialog actions do **not** take an icon` child.
Common extension points: OrderPage-OrderDetails, ProductPage-General, ProductPage-Images, CustomerPage-Profile, ContentPage-General, CatalogPage-CategoryGeneral.
Form Extensions
Add fields to existing BM search forms (e.g. order or customer search).
Invoice Number
Rechnungsnummer
Sync Status
All
Synced
Pending
Failed
Minimum Quantity
id matches the target form (e.g. order-search, customer-search). valueinput requires type (string, int, or double) and name; defaultvalue is optional. Each valueinput has localized ` children and optional ` children for dropdowns.
Controller Example
Controllers live in controllers/ and export functions marked .public = true. Use */cartridge/... requires so logic can be shared, and gate page nodes on the BM session.
'use strict';
var ISML = require('dw/template/ISML');
var URLUtils = require('dw/web/URLUtils');
var Logger = require('dw/system/Logger');
var log = Logger.getLogger('bm', 'ACME');
/**
* Render the dashboard. Wired to .
*/
function Start() {
// BM nodes should only be reachable in an authenticated BM session
if (!session.userAuthenticated) {
response.setStatus(403);
return;
}
ISML.renderTemplate('extensions/acme/dashboard', {
stats: getStats()
});
}
Start.public = true;
module.exports.Start = Start;
/**
* Process an action, then redirect back to the dashboard.
*/
function Process() {
var action = request.httpParameterMap.action.stringValue;
try {
performAction(action);
response.redirect(URLUtils.url('ACME-Start', 'message', 'Done'));
} catch (e) {
log.error('Action failed: {0}', e.message);
response.redirect(URLUtils.url('ACME-Start', 'error', e.message));
}
}
Process.public = true;
module.exports.Process = Process;
/**
* Lightweight endpoint called by page JS — registered under NoPermissionCheck.
*/
function Keepalive() {
response.setContentType('application/json');
response.writer.print('{"ok":true}');
}
Keepalive.public = true;
module.exports.Keepalive = Keepalive;
Every node referenced by ` or must be exported and .public = true`, or BM returns 403.
Template Example
BM templates render under templates/default/extensions/. Reference the node via URLUtils.url('Controller-Node', ...).
ACME Dashboard
${request.httpParameterMap.message.stringValue}
MetricValue
${stat.label}${stat.value}
Refresh
Decorating with application/MenuFrame (and including inc/Modules) gives the page the standard BM chrome. A plain HTML document also works if you want a fully custom page (the modern qlabs-style approach mounts a single-page app into a container template).
Attaching to Existing BM Menus
Instead of a new menu item, point a menu action's menupath at a core BM menu id. The full list of core ids is in the reference; the most useful:
| Menu | id | site | |------|------|--------| | Site Preferences | site-prefs | true | | Merchant Tools › Products and Catalogs | prod-cat | true | | Merchant Tools › Ordering | orders | true | | Administration › Operations | operations | false | | Administration › Site Development | studio | false | | Administration › Global Preferences | global-prefs | false |
ACME Preferences
> Reusing a core BM id for your own menuitem/menuaction hides your extension. Always use unique, prefixed ids and only reference core ids via menupath.
Enabling the Extension
- Add the cartridge to the Business Manager site's cartridge path:
- Administration › Sites › Manage Sites › Business Manager › Settings
- Add the cartridge id (e.g.
bm_my_extension) to the cartridge path.
- Grant the module to roles:
- Administration › Organization › Roles › (role) › Business Manager Modules
- Enable the custom module (it appears once the extension is on the path).
- Nodes registered under
NoPermissionCheckare reachable without this grant.
Best Practices
- Validate against the XSD with
b2c docs schema bmextwhenever unsure. - Prefix all ids to avoid colliding with (and accidentally hiding behind) core BM ids.
- Register every node a page uses in
sub-pipelines, or it 403s. - Use
NoPermissionCheckonly for endpoints that are safe for any authenticated BM user (keepalive, service workers, polling). - Keep BM cartridges separate from storefront cartridges.
- Gate page nodes on
session.userAuthenticated. - Test with a non-admin role to confirm your permission boundaries.
Detailed Reference
- [Extensions XML Reference](references/EXTENSIONS-XML.md) — complete schema, every element/attribute, the
NoPermissionCheckpattern, and the full core BM menu-id table.
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: SalesforceCommerceCloud
- Source: SalesforceCommerceCloud/b2c-developer-tooling
- License: Apache-2.0
- Homepage: https://salesforcecommercecloud.github.io/b2c-developer-tooling/
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.