Bring Your Own EDS Project bring-your-own-eds

IMPORTANT
Adobe LLM Apps is currently in Beta.
Features, workflows, and UI shown here do not necessarily represent the final state of the product. To join the Beta, send an email to llm-apps-beta@adobe.com.

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

  1. Preview the EDS page directly and verify its sample-data fallback.
  2. Test the handler locally and compare its structuredContent with the shape expected by the block.
  3. Deploy the app to staging.
  4. Invoke the action from ChatGPT.
  5. 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.

recommendation-more-help
llm-apps-help-main-toc