From Hallucinated APIs to Shippable Banners: Adobe Express MCP Lab + Cloud Del Norte Builder Banner

August 31, 2026 · 7 min · 1443 words · Bryan Chasko

tl;dr for leadership: Adobe Express add-ons let a community builder ship to millions from a static panel (manifest.json + index.html + code.js). Without grounding, every LLM invents APIs that fail sideload. The lab at chasko-labs/adobe-express-mcp-lab wires the official @adobe/express-developer-mcp@1.0.0 (stdio, no auth, 197kB) via a heraldstack bridge so generation is correct first try. We proved it by building a brand-faithful 1440×400 AWS Builder Center banner for Cloud Del Norte — dark nebula #00002a→#30006a, star-on-mountain mark, Cinzel + JetBrains Mono — generated via editor.createRectangle + makeColorFill and headless-verified at 60 fps.

AWS Builder Center banner — Cloud Del Norte design system, 1440×400

the banner above is the shippable output — 1440×400 master SVG (also 1440×400 PNG, 112KB) — displayed at the top of its use case. it lives at docs/assets/aws-builder-center-banner.svg in the lab and is the cover for builder.aws.com/community/@alias.

executive summary

problem: Builder Center profiles need a branded cover, and Express add-ons are the only native way to ship branded designs to millions. Every LLM asked to “build me an Express add-on” hallucinates (adobe.addOn.createShape, app.document.addRectangle, wrong apiVersion) and wastes 2h debugging an API that never existed.

lab: 5 async agents in parallel, no mutual deps, each returns a bounded artifact to chasko-labs/adobe-express-mcp-lab — package truth, ecosystem mental model, official vs 7 community MCP landscape, heraldstack wiring, and learnings. 3 commits to green, then visuals + guided readme + hello-world + builder banner.

proof: addons/hello-world (132 lines, manifestVersion:2, documentSandbox: code.js, import { editor } from "express-document-sdk"createRectangle + makeColorFill) sideloads first try. addons/aws-builder-banner reuses the same grounded pattern to generate the 1440×400 banner below — headless-verified at 60 fps with all 4 controls exercised (theme, headline, subhead, tagline) and clearArtboardregenerate cycle.

takeaway: MCP is not automation of Express — it is grounding for the LLM. The lab is the reproducible starter so any heraldstack agent stops inventing and starts shipping.

what is adobe express and why a community builder cares

What is Adobe Express — editor, canvas, add-on panel

Adobe Express is Adobe’s browser-native design platform at express.adobe.com — Canva-like but with Adobe DNA: drag/resize/animate canvas, 100M+ templates, Adobe Stock, and Firefly generative AI, all in the browser. Free tier covers most creation; Premium unlocks brand kits and premium templates.

For a builder, the leverage is distribution: an add-on is manifest.json + index.html + code.js, installed in one click from express.adobe.com/add-ons, served from Adobe’s CDN, and auto-updated. No backend, no infra. Today (2026) you can ship:

whatexample
brand toolsone-click brand palette → branded rectangles/text via editor.createRectangle
data-drivenAirtable/Notion → 10 flyers at once
ai workflowsfetch from OpenAI/Claude in the iframe → proxy to sandbox → draw
content packssticker libraries, localized templates
utilityQR, bulk resize, background wrapper

Free to author and list; review is days; Fund for Design grants exist. Limits today: manifestVersion:2, single panel entry, requirements.apps: [{name: "Express", apiVersion: 1}], no arbitrary filesystem. Trade-off: safe, fast review vs Cep’s CEF+Node and UXP’s native plugins — see the lab’s ecosystem comparison.

what the mcp unlocks

Challenge → Solve: without MCP you hallucinate, with MCP you ship

without @adobe/express-developer-mcp:

// invented — sideload rejects
adobe.addOn.createShape({ type: "rect", x: 100, y: 20 });
app.document.addRectangle(200, 150);
// manifest: { "apiVersion": 2 } // wrong key
// Error: express-document-sdk not found

with @adobe/express-developer-mcp@1.0.0 (stdio via npx -y @adobe/express-developer-mcp@latest --yes, no auth, type: module, bin: adobe-express-developer-mcp-server → bin/run.js, 14 files, sha512 kqEGiGSb..., 2026-01-14):

{
	"mcpServers": {
		"adobe-express-developer": {
			"command": "npx",
			"args": ["@adobe/express-developer-mcp@latest", "--yes"]
		}
	}
}

The LLM gains:

  • semantic search over developer.adobe.com/express/add-ons/docs
  • typescript types from @adobe/ccweb-add-on-sdk-types@1.40.0editor.createRectangle, editor.context.insertionParent, editor.makeColorFill
  • manifest schema groundingmanifestVersion: 2, documentSandbox: "code.js", permissions.sandbox/oauth

Result: the code below is correct first try. The lab exists so you never debug a hallucinated API again.

hello-world — from challenge thrown to challenge solved

Hello-World 5-step flow with screenshots at each step

challenge: “I need a button inside Express that drops my branded 200×150 rectangle at 100,20 — no user should pick color or size.”

the 5 screenshots above are the proof (left to right):

  1. problem — user request: branded rect, fixed size/position.

  2. ask mcp — prompt grounded in the IDE (Cursor/Claude/VS Code with adobe-express-developer connected):

    using the adobe express mcp, show me manifestVersion 2 and the document
    sandbox pattern to append a rectangle at 100,20. generate the 3 files.
    

    MCP returns editor.createRectangle + makeColorFill + insertionParent.children.append and the bridge exposeApi/getApi — no hallucination.

  3. generate — LLM writes addons/hello-world/ — 132 lines, manifest.json ✓ V2, index.html ✓ SDK CDN, code.jseditor. Copy-paste ready.

  4. sideload — zip 3 files → upload at developer.adobe.com/console → Preview in Express (free, 30s).

  5. solve — in Express: open document → Add-ons panel → Hello WorldDraw 200×150 rectangle → blue rect at 100,20 via insertionParent.children.append(rect). The panel + canvas in screenshot 5 is the receipt.

code samples — the grounded pattern

manifest (addons/hello-world/manifest.json):

{
	"manifestVersion": 2,
	"version": "1.0.0",
	"requirements": { "apps": [{ "name": "Express", "apiVersion": 1 }] },
	"entryPoints": [
		{
			"type": "panel",
			"id": "hello-world",
			"main": "index.html",
			"documentSandbox": "code.js",
			"permissions": { "sandbox": ["allow-popups"] }
		}
	]
}

iframe (index.html — full browser APIs):

<script type="module">
	import addOnUISdk from "https://express.adobe.com/static/add-on-sdk/sdk.js";
	await addOnUISdk.ready;
	document.getElementById("draw").onclick = async () => {
		const api = await addOnUISdk.instance.runtime.getApi("sandbox");
		await api.createRectangle({ width: 200, height: 150, x: 100, y: 20 });
	};
</script>

sandbox (code.js — synchronous document thread, per lab):

import { editor } from "express-document-sdk";

function fromRGB(r, g, b, a = 1) {
	return { red: r, green: g, blue: b, alpha: a };
}

async function createRectangle({ width = 200, height = 150, x = 100, y = 20 } = {}) {
	const parent = editor.context.insertionParent;
	const rect = editor.createRectangle();
	rect.width = width;
	rect.height = height;
	rect.translation = { x, y };
	rect.fill = editor.makeColorFill(fromRGB(0.32, 0.52, 0.92, 1));
	parent.children.append(rect);
	return { ok: true };
}

Bridge: runtime.exposeApi({ createRectangle }) in sandbox ↔ runtime.getApi("sandbox") in iframe (Communication APIs). Iframe does fetch/OAuth; sandbox does editor — never mixed.

MCP stdio architecture — IDE to npx bridge to MCP server to LLM to docs+types

iframe vs sandbox — where fetch vs editor live

cloud del norte builder banner — the use case that proves reusability

The hello-world proved grounding; the banner proves brand-faithfulness at production size.

design system source: cloud-del-norte-website/src/styles/tokens.css--cdn-navy #00002a--cdn-purple-deep #30006a gradient (dark nebula), --cdn-purple #5a1f8a, --cdn-violet #9060f0, --cdn-lavender #d7c7ee, --cdn-gold #c9a23f, --cdn-aws-orange #ff9900, fonts Cinzel + JetBrains Mono. Star-on-mountain mark from art/images/cloud-del-norte-star-logo/src/cdn-star-logo-clean.svg (353 paths, 5-role fill-remap, white outline + violet lattice).

spec (atomized): spec/use-cases/aws-builder-center-banner-spec.md1440×400 master SVG + 1440×400 PNG (rsvg-convert -w 1440 -h 400, 112KB, <5MB), safe center 1200×400, mobile crop 800×400, palette eyedropped to tokens, a11y white-on-navy 18:1 AAA.

the banner (near top of this post and the lab’s README):

The SVG master is embedded above — nebula gradient, star at (180,200), wordmark Cloud Del Norte (Cinzel 54), subhead AWS User Group • El Paso Borderplex (Mono 16), CTA pill View Builders → (#ff9900). PNG fallback is aws-builder-center-banner-1440x400.png for the builder.aws.com upload (no public dimension spec; inferred 1440×400 from og 1200×630 + Banner_Home_* .svg + typical 1440×400/1200×230 builder covers, with central 1200 safe).

add-on that generates it: addons/aws-builder-banner/ — same grounded pattern as hello-world, but at banner scale (1440×400), theme toggle el-paso-nights dark vs gypsum-sands light #ede5d4, headline/subhead/tagline controls. Code highlight:

// code.js — generateBanner({ headline, subhead, theme, width:1440, height:400 })
const bg = editor.createRectangle();
bg.width = 1440;
bg.height = 400;
bg.fill = editor.makeColorFill(fromRGB(0, 0, 0.164, 1)); // #00002a
parent.children.append(bg);
const overlay = editor.createRectangle();
overlay.fill = editor.makeColorFill(fromRGB(0.188, 0, 0.416, 0.55)); // #30006a
parent.children.append(overlay);
const cta = editor.createRectangle();
cta.width = 200;
cta.height = 34;
cta.translation = { x: 1080, y: 285 };
cta.fill = editor.makeColorFill(fromRGB(1, 0.6, 0, 1)); // #ff9900

Headless-verified in a real Express SDK mock (see screenshots section): all 4 controls driven, 6 rects via editor.createRectangle+makeColorFill → insertionParent.children.append per generation, clearArtboard: removed 6 → regenerated, 60 fps responsive — captured in the background browser screenshot.

screenshots

MCP stdio wiring — registry to bridge to dispatcher to IDE

Official vs community MCP map — Express docs grounding vs live canvas automation

Landscape is the guardrail: Express MCP = docs grounding (this lab) vs Photoshop/Animate MCPs = live canvas automation (matrayu/adobe-mcp WebSocket+UXP, vanhock/animate-mcp CEP+JSFL) — different security models, different review. Don’t mix them.

whatwhere
lab + guided toc (14 sections)github.com/chasko-labs/adobe-express-mcp-lab
package truth (1.0.0, sha512, stdio)docs/mcp-technical.md
iframe vs sandbox mental modeldocs/ecosystem.md
official vs 7 community landscapedocs/adobe-mcp-landscape.md
heraldstack wiring (registry + bridge)spec/wiring.md
builder banner use case + spec + plandocs/use-cases/aws-builder-center-banner.md + spec/use-cases/aws-builder-center-banner-*
hello-world + builder banner add-onsaddons/hello-world/ + addons/aws-builder-banner/
banner mastersdocs/assets/aws-builder-center-banner.svg + .png

try it:

npx -y @adobe/express-developer-mcp@latest --yes
# or: bash ./mcp/adobe-express-bridge.sh
# wire to Cursor (~/.cursor/mcp.json) / Claude (claude_desktop_config.json) / VS Code (~/.vscode/mcp.json) — restart
# then: "using the adobe express mcp, generate a hello-world add-on"
# sideload: zip manifest.json index.html code.js → developer.adobe.com/console → Preview → Express

The lab is MIT, public, and prettier-clean — fork the banner, recolor fill: [0.32, 0.52, 0.92, 1] to your brand, bump version, re-zip, ship to Builder Center in 5 minutes instead of 2 hours. Questions → Discord nc3QDyFeb4 (Adobe Express Add-on Developers).

lab head at write time: c5fd1e1. blog branch: blog/adobe-express-mcp-lab-cloud-del-norte-banner — team will merge.