Skip to content
CP-MCP
Get Support

Generating portal images

core.generate_image is a utility on the core object that renders a branded PNG for a Cloudpath tenant-property portal or management portal. It runs a small image pipeline inside the Worker and hands the PNG bytes back to your code as base64, so you can upload them through core.call in the same code_mode invocation — the bytes never round-trip the MCP transport.

Each target maps 1:1 to a Cloudpath upload endpoint. Logos are 4:1 banners; favicons are square monogram tiles.

TargetSizeUploads to
tenant_logo1024×256POST /properties/{guid}/uiConfiguration/logoFile
tenant_favicon128×128POST /properties/{guid}/uiConfiguration/favicon
mgmt_logo1024×256POST /mgmtPortals/{guid}/uiConfiguration/logoFile
mgmt_favicon128×128POST /mgmtPortals/{guid}/uiConfiguration/favicon

Logos combine an AI-generated background (Workers AI, @cf/black-forest-labs/flux-1-schnell) with a typography overlay. Favicons skip the AI step entirely and render a pure monogram tile.

ParameterRequired forNotes
targetallOne of the four targets above
primaryTextlogo targetsThe main line of text
backgroundPromptlogo targetsSubject and mood for the AI background
monogramfavicon targets1–2 characters
primaryColorallHex, e.g. #0D2B45
accentColorallHex
secondaryTextoptionalSecond line on logos
textColoroptionalHex, default #FFFFFF
fontoptionalinter (default), playfair, montserrat, dm-serif
layoutoptionalleft-gradient, center-vignette, bottom-bar (defaults: left-gradient for logos, center-vignette for favicons)
{ imageBase64, mimeType, width, height, suggestedFilename, model, aiGenerationMs, overlayMs }

model is the Workers AI model used, or null for favicons (which make no AI call). suggestedFilename is the value to use in the multipart upload’s text field — see below.

The four uiConfiguration upload endpoints expect multipart/form-data, and uploading them has two gotchas that will silently corrupt the result if missed:

Second, because the isolate marshals tool arguments through JSON.stringify, you cannot pass a Uint8Array, FormData, or Blob as body — assemble the multipart body yourself, base64-encode it, and pass it as bodyBase64 with the Content-Type (including the boundary) set in headers. See Calling conventions.

// Generate + upload a property portal logo in a single code_mode call.
async () => {
const img = await core.generate_image({
target: 'tenant_logo',
primaryText: 'HARBORVIEW RESIDENCES',
backgroundPrompt: 'aerial view of a luxury waterfront community at golden hour',
primaryColor: '#0D2B45',
accentColor: '#C8960C',
font: 'playfair',
});
// Decode the returned PNG bytes.
const bin = atob(img.imageBase64);
const png = new Uint8Array(bin.length);
for (let i = 0; i < bin.length; i++) png[i] = bin.charCodeAt(i);
// Build the multipart body: a `filename` text part + the `file` image part.
const boundary = '----cp-mcp-' + Math.random().toString(36).slice(2);
const enc = new TextEncoder();
const head = enc.encode(
`--${boundary}\r\nContent-Disposition: form-data; name="filename"\r\n\r\n${img.suggestedFilename}\r\n` +
`--${boundary}\r\nContent-Disposition: form-data; name="file"; filename="${img.suggestedFilename}"\r\nContent-Type: image/png\r\n\r\n`
);
const tail = enc.encode(`\r\n--${boundary}--\r\n`);
const body = new Uint8Array(head.length + png.length + tail.length);
body.set(head, 0);
body.set(png, head.length);
body.set(tail, head.length + png.length);
// Base64-encode in chunks (spread would overflow the call stack), one btoa at the end.
let s = '';
for (let i = 0; i < body.length; i += 0x8000) {
const sub = body.subarray(i, i + 0x8000);
for (let j = 0; j < sub.length; j++) s += String.fromCharCode(sub[j]);
}
const r = await core.call({
method: 'POST',
path: '/properties/{guid}/uiConfiguration/logoFile',
pathParams: { guid: 'your-property-guid' },
headers: { 'Content-Type': `multipart/form-data; boundary=${boundary}` },
bodyBase64: btoa(s),
});
return { uploaded: r.ok, status: r.status, model: img.model };
}