Bring Your Own EDS Project bring-your-own-eds
Use this guide when you already have an Edge Delivery Services (EDS) project or when you created an app without building it automatically.
If the platform created your widget automatically, follow Customize a generated widget instead. The generated project already includes the SDK files, block, content, and action configuration described here.
Journey: Prepare the EDS project → install the SDK → build and publish the block → configure the action → deploy and test.
Before you begin
You need:
- An EDS repository with AEM Code Sync installed.
- Permission to add dependencies and create blocks in that repository.
- Permission to configure response headers for the EDS site.
- An action in LLM Apps with a handler that returns
structuredContent.
Install the LLM Apps SDK
From the EDS project root:
npm install @adobe/llmapps-sdk
The package copies the widget entry point and bridge implementation into the project:
scripts/
├── aem-embed.js
└── llmapps-sdk.js
The Script URL used by the action points to scripts/aem-embed.js.
Create the widget block
Create a block for the action:
blocks/
└── search-products/
├── search-products.js
└── search-products.css
Export the standard EDS decorate function with the connected bridge as its second argument:
export default async function decorate(block, bridge) {
if (bridge) {
bridge.applyHostStyles();
}
const result = bridge ? await bridge.toolResult : null;
const products = result?.structuredContent?.products ?? [];
const list = document.createElement('ul');
products.forEach((product) => {
const item = document.createElement('li');
item.textContent = String(product.name ?? 'Product');
list.append(item);
});
block.replaceChildren(list);
if (bridge) {
bridge.autoResize(block);
}
}
Use DOM APIs that encode text values. Do not concatenate external data into HTML.
Author and publish the widget page
Create one EDS page for the widget and add the block to that page. Publish the page.
The live page URL becomes the action’s Widget URL:
https://main--<repo>--<owner>.aem.live/<widget-page>
The page path does not need to match the action name, but a consistent convention makes the project easier to maintain.
Configure CORS
The widget loads the EDS page plus scripts, styles, blocks, and media across origins. Configure the header for the EDS site:
{
"/**": [
{
"key": "access-control-allow-origin",
"value": "<allowed-host-origin>"
}
]
}
Use the specific host origin required by your supported LLM platform. Use * only when the widget is intentionally public, does not use credentialed cross-origin requests, and your security requirements allow it.
For EDS configuration details, see the Configuration Service.
Configure the action
In LLM Apps, open the action and select Widget Metadata.
Enter:
-
Script URL
code language-text https://main--<repo>--<owner>.aem.live/scripts/aem-embed.js -
Widget URL
code language-text https://main--<repo>--<owner>.aem.live/<widget-page>
Configure CSP domains and browser permissions using least privilege. Add only origins and capabilities required by the widget.
For field definitions, see Action and widget fields.
Test the integration
- Preview the EDS page directly and verify its sample-data fallback.
- Test the handler locally and compare its
structuredContentwith the shape expected by the block. - Deploy the app to staging.
- Invoke the action from ChatGPT.
- Verify loading, success, empty, and error states.
If the page works directly but not in the LLM platform, check CORS, CSP, HTTPS URLs, and the structuredContent shape. See Troubleshooting.