facebook-ads-mcp-server
Version:
Universal Facebook Ads Server - Supports MCP, OpenAI Function Calling, and Gemini Function Calling
139 lines (120 loc) • 4.66 kB
JavaScript
import { FacebookAPIClient } from '../utils/facebook-api.js';
import { ValidationSchemas, validateParameters } from '../utils/validation.js';
import { createErrorResponse } from '../utils/error-handler.js';
import axios from 'axios';
/**
* Get ad creatives for specific ad IDs
* Focused tool that only fetches creative assets
*/
export async function getAdCreatives(args) {
try {
const validatedArgs = validateParameters(ValidationSchemas.adCreatives, args);
const { ad_ids, include_images = true } = validatedArgs;
const facebookAPI = new FacebookAPIClient();
console.error(`🖼️ Fetching creatives for ${ad_ids.length} ads`);
// Batch request for efficiency
const batchRequests = ad_ids.map(adId => ({
method: 'GET',
relative_url: `${adId}?fields=id,name,creative{id,image_url,thumbnail_url,object_story_spec{video_data{image_url},link_data{name,message,caption,call_to_action}},asset_feed_spec{images{url},titles{text},bodies{text},call_to_action_types}}`
}));
const batchResponse = await facebookAPI.makeBatchRequest(batchRequests);
const ads = [];
// Process batch response
for (let i = 0; i < ad_ids.length; i++) {
const result = batchResponse[i];
if (result?.code === 200 && result.body) {
const adData = JSON.parse(result.body);
const creative = adData.creative || {};
const processedAd = {
ad_id: adData.id,
ad_name: adData.name,
creative: {
id: creative.id,
type: detectCreativeType(creative),
image_url: getBestImageUrl(creative),
headline: extractHeadline(creative),
body: extractBody(creative),
call_to_action: extractCTA(creative)
}
};
ads.push(processedAd);
} else {
console.error(`Failed to get creative for ad ${ad_ids[i]}: ${result?.code}`);
}
}
// Download and embed images if requested
if (include_images && ads.length > 0) {
console.error(`📥 Downloading images for ${ads.length} ads...`);
await Promise.all(ads.map(async (ad) => {
if (ad.creative.image_url) {
try {
const response = await axios.get(ad.creative.image_url, {
responseType: 'arraybuffer',
timeout: 5000,
headers: {
'User-Agent': 'Mozilla/5.0 (compatible; FacebookAds/1.0)'
}
});
const base64 = Buffer.from(response.data).toString('base64');
const mimeType = response.headers['content-type'] || 'image/jpeg';
ad.creative.image_data = `data:${mimeType};base64,${base64}`;
console.error(`✅ Downloaded image for ad ${ad.ad_id}`);
} catch (error) {
console.error(`❌ Failed to download image for ad ${ad.ad_id}: ${error.message}`);
}
}
}));
}
return {
content: [{
type: 'text',
text: JSON.stringify({
summary: `Retrieved ${ads.length} ad creatives`,
ads: ads
}, null, 2)
}]
};
} catch (error) {
console.error('Error in getAdCreatives:', error);
return createErrorResponse(error);
}
}
// Helper functions
function getBestImageUrl(creative) {
// Priority: full-size images from various sources
// 1. Dynamic creative images (usually full resolution)
if (creative.asset_feed_spec?.images?.[0]?.url) {
return creative.asset_feed_spec.images[0].url;
}
// 2. Video thumbnail (full resolution)
if (creative.object_story_spec?.video_data?.image_url) {
return creative.object_story_spec.video_data.image_url;
}
// 3. Direct image_url (may be full or thumbnail)
if (creative.image_url) {
return creative.image_url;
}
// 4. Fallback to thumbnail
return creative.thumbnail_url;
}
function detectCreativeType(creative) {
if (creative.object_story_spec?.video_data) return 'video';
if (creative.asset_feed_spec?.videos?.length > 0) return 'video';
if (creative.asset_feed_spec?.images?.length > 1) return 'carousel';
return 'image';
}
function extractHeadline(creative) {
return creative.object_story_spec?.link_data?.name ||
creative.asset_feed_spec?.titles?.[0]?.text ||
null;
}
function extractBody(creative) {
return creative.object_story_spec?.link_data?.message ||
creative.asset_feed_spec?.bodies?.[0]?.text ||
null;
}
function extractCTA(creative) {
return creative.object_story_spec?.link_data?.call_to_action?.type ||
creative.asset_feed_spec?.call_to_action_types?.[0] ||
null;
}