MCPcopy Create free account
hub / github.com/adcontextprotocol/adcp / handleBuildCreative

Function handleBuildCreative

server/src/training-agent/task-handlers.ts:3118–3242  ·  view source on GitHub ↗
(args: ToolArgs, ctx: TrainingContext)

Source from the content-addressed store, hash-verified

3116}
3117
3118export async function handleBuildCreative(args: ToolArgs, ctx: TrainingContext): Promise<BuildCreativeResponse & { pricing_option_id?: string; vendor_cost?: number; currency?: string; consumption?: Record<string, unknown>; governance_context?: string }> {
3119 const req = args as unknown as BuildCreativeArgs;
3120 const session = await getSession(sessionKeyFromArgs(req as unknown as ToolArgs, ctx.mode, ctx.userId, ctx.moduleId));
3121 const agentUrl = getAgentUrl();
3122 const formats = getFormats();
3123 const rawGovCtx = (req as unknown as Record<string, unknown>).governance_context;
3124 const governanceContext = typeof rawGovCtx === 'string' && rawGovCtx.length <= 4096 ? rawGovCtx : undefined;
3125 const validFormatIds = new Map(formats.map(f => [f.format_id.id, f]));
3126
3127 // Determine target formats (cap at 50 to prevent response amplification)
3128 const MAX_TARGET_FORMATS = 50;
3129 const targetIds: FormatID[] = req.target_format_ids?.length
3130 ? req.target_format_ids.slice(0, MAX_TARGET_FORMATS)
3131 : req.target_format_id
3132 ? [req.target_format_id]
3133 : [];
3134
3135 // Mode 1: Library retrieval (creative_id)
3136 if (req.creative_id) {
3137 const creative = session.creatives.get(req.creative_id) ?? getComplianceCreative(req.creative_id);
3138 if (!creative) {
3139 return {
3140 errors: [{ code: 'NOT_FOUND', message: `Creative "${req.creative_id}" not found. Use sync_creatives to upload or list_creatives to browse.` }],
3141 };
3142 }
3143
3144 const formatId = targetIds[0] || creative.formatId;
3145 const format = validFormatIds.get(formatId.id);
3146 const { w, h } = getDimensions(format);
3147
3148 const base = {
3149 creative_manifest: {
3150 format_id: { agent_url: agentUrl, id: formatId.id },
3151 assets: buildHtmlAssets(`<!-- AdCP Training Agent tag for ${escapeHtmlAttr(req.creative_id!)} -->\n<div data-adcp-creative="${escapeHtmlAttr(req.creative_id!)}" data-format="${escapeHtmlAttr(formatId.id)}"${req.media_buy_id ? ` data-media-buy="${escapeHtmlAttr(req.media_buy_id)}"` : ''}${req.package_id ? ` data-package="${escapeHtmlAttr(req.package_id)}"` : ''} style="width:${w}px;height:${h}px;background:#f0f0f0;display:flex;align-items:center;justify-content:center;font-family:sans-serif;font-size:14px;color:#666;">Ad: ${escapeHtmlAttr(creative.name || req.creative_id!)}</div>`),
3152 },
3153 };
3154
3155 // Return pricing when account is provided (paid creative agent mode)
3156 if (req.account) {
3157 const pricing = getCreativePricing(req.account, creative);
3158 creative.pricingOptionId = pricing.pricing_option_id;
3159 return {
3160 ...base,
3161 pricing_option_id: pricing.pricing_option_id,
3162 vendor_cost: 0, // CPM-priced: cost accrues at serve time
3163 currency: pricing.currency,
3164 consumption: {},
3165 ...(governanceContext && { governance_context: governanceContext }),
3166 };
3167 }
3168
3169 return { ...base, ...(governanceContext && { governance_context: governanceContext }) };
3170 }
3171
3172 // Mode 2: Stateless transformation (creative_manifest + target_format_id)
3173 if (req.creative_manifest) {
3174 const rawAssets = req.creative_manifest.assets;
3175 const inputAssetCount = Array.isArray(rawAssets) ? rawAssets.length : Object.keys(rawAssets || {}).length;

Callers

nothing calls this directly

Calls 10

getSessionFunction · 0.85
sessionKeyFromArgsFunction · 0.85
getAgentUrlFunction · 0.85
getFormatsFunction · 0.85
getComplianceCreativeFunction · 0.85
getDimensionsFunction · 0.85
buildHtmlAssetsFunction · 0.85
getCreativePricingFunction · 0.85
escapeHtmlAttrFunction · 0.70
getMethod · 0.45

Tested by

no test coverage detected