Skip to main content
Glama

create_ad_creative

Generate new Meta ad creatives by specifying image hash, ad copy, headlines, descriptions, and call-to-action. Supports dynamic creative testing and Instagram placements.

Instructions

Create a new ad creative using an uploaded image hash. Args: access_token: Meta API access token (optional - will use cached token if not provided) account_id: Meta Ads account ID (format: act_XXXXXXXXX) name: Creative name image_hash: Hash of the uploaded image page_id: Facebook Page ID to be used for the ad link_url: Destination URL for the ad message: Ad copy/text headline: Single headline for simple ads (cannot be used with headlines) headlines: List of headlines for dynamic creative testing (cannot be used with headline) description: Single description for simple ads (cannot be used with descriptions) descriptions: List of descriptions for dynamic creative testing (cannot be used with description) dynamic_creative_spec: Dynamic creative optimization settings call_to_action_type: Call to action button type (e.g., 'LEARN_MORE', 'SIGN_UP', 'SHOP_NOW') instagram_actor_id: Optional Instagram account ID for Instagram placements Returns: JSON response with created creative details

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
access_tokenNo
account_idNo
call_to_action_typeNo
descriptionNo
descriptionsNo
dynamic_creative_specNo
headlineNo
headlinesNo
image_hashNo
instagram_actor_idNo
link_urlNo
messageNo
nameNo
page_idNo

Implementation Reference

  • Primary handler function decorated with @mcp_server.tool() which registers the 'create_ad_creative' tool. Handles creation of Meta ad creatives supporting both simple and dynamic formats with image hash, text elements, page auto-discovery, validation, and API calls.
    @mcp_server.tool() @meta_api_tool async def create_ad_creative( account_id: str, image_hash: str, access_token: Optional[str] = None, name: Optional[str] = None, page_id: Optional[str] = None, link_url: Optional[str] = None, message: Optional[str] = None, headline: Optional[str] = None, headlines: Optional[List[str]] = None, description: Optional[str] = None, descriptions: Optional[List[str]] = None, dynamic_creative_spec: Optional[Dict[str, Any]] = None, call_to_action_type: Optional[str] = None, instagram_actor_id: Optional[str] = None ) -> str: """ Create a new ad creative using an uploaded image hash. Args: account_id: Meta Ads account ID (format: act_XXXXXXXXX) image_hash: Hash of the uploaded image access_token: Meta API access token (optional - will use cached token if not provided) name: Creative name page_id: Facebook Page ID to be used for the ad link_url: Destination URL for the ad message: Ad copy/text headline: Single headline for simple ads (cannot be used with headlines) headlines: List of headlines for dynamic creative testing (cannot be used with headline) description: Single description for simple ads (cannot be used with descriptions) descriptions: List of descriptions for dynamic creative testing (cannot be used with description) dynamic_creative_spec: Dynamic creative optimization settings call_to_action_type: Call to action button type (e.g., 'LEARN_MORE', 'SIGN_UP', 'SHOP_NOW') instagram_actor_id: Optional Instagram account ID for Instagram placements Returns: JSON response with created creative details """ # Check required parameters if not account_id: return json.dumps({"error": "No account ID provided"}, indent=2) if not image_hash: return json.dumps({"error": "No image hash provided"}, indent=2) if not name: name = f"Creative {int(time.time())}" # Ensure account_id has the 'act_' prefix if not account_id.startswith("act_"): account_id = f"act_{account_id}" # Enhanced page discovery: If no page ID is provided, use robust discovery methods if not page_id: try: # Use the comprehensive page discovery logic from get_account_pages page_discovery_result = await _discover_pages_for_account(account_id, access_token) if page_discovery_result.get("success"): page_id = page_discovery_result["page_id"] page_name = page_discovery_result.get("page_name", "Unknown") print(f"Auto-discovered page ID: {page_id} ({page_name})") else: return json.dumps({ "error": "No page ID provided and no suitable pages found for this account", "details": page_discovery_result.get("message", "Page discovery failed"), "suggestions": [ "Use get_account_pages to see available pages", "Use search_pages_by_name to find specific pages", "Provide a page_id parameter manually" ] }, indent=2) except Exception as e: return json.dumps({ "error": "Error during page discovery", "details": str(e), "suggestion": "Please provide a page_id parameter or use get_account_pages to find available pages" }, indent=2) # Validate headline/description parameters - cannot mix simple and complex if headline and headlines: return json.dumps({"error": "Cannot specify both 'headline' and 'headlines'. Use 'headline' for single headline or 'headlines' for multiple."}, indent=2) if description and descriptions: return json.dumps({"error": "Cannot specify both 'description' and 'descriptions'. Use 'description' for single description or 'descriptions' for multiple."}, indent=2) # Validate dynamic creative parameters (plural forms only) if headlines: if len(headlines) > 5: return json.dumps({"error": "Maximum 5 headlines allowed for dynamic creatives"}, indent=2) for i, h in enumerate(headlines): if len(h) > 40: return json.dumps({"error": f"Headline {i+1} exceeds 40 character limit"}, indent=2) if descriptions: if len(descriptions) > 5: return json.dumps({"error": "Maximum 5 descriptions allowed for dynamic creatives"}, indent=2) for i, d in enumerate(descriptions): if len(d) > 125: return json.dumps({"error": f"Description {i+1} exceeds 125 character limit"}, indent=2) # Prepare the creative data creative_data = { "name": name } # Choose between asset_feed_spec (dynamic creative) or object_story_spec (traditional) # ONLY use asset_feed_spec when user explicitly provides plural parameters (headlines/descriptions) if headlines or descriptions: # Use asset_feed_spec for dynamic creatives with multiple variants asset_feed_spec = { "ad_formats": ["SINGLE_IMAGE"], "images": [{"hash": image_hash}], "link_urls": [{"website_url": link_url if link_url else "https://facebook.com"}] } # Handle headlines if headlines: asset_feed_spec["headlines"] = [{"text": headline_text} for headline_text in headlines] # Handle descriptions if descriptions: asset_feed_spec["descriptions"] = [{"text": description_text} for description_text in descriptions] # Add message as primary_texts if provided if message: asset_feed_spec["primary_texts"] = [{"text": message}] # Add call_to_action_types if provided if call_to_action_type: asset_feed_spec["call_to_action_types"] = [call_to_action_type] creative_data["asset_feed_spec"] = asset_feed_spec # For dynamic creatives, we need a simplified object_story_spec creative_data["object_story_spec"] = { "page_id": page_id } else: # Use traditional object_story_spec with link_data for simple creatives creative_data["object_story_spec"] = { "page_id": page_id, "link_data": { "image_hash": image_hash, "link": link_url if link_url else "https://facebook.com" } } # Add optional parameters if provided if message: creative_data["object_story_spec"]["link_data"]["message"] = message # Add headline (singular) to link_data if headline: creative_data["object_story_spec"]["link_data"]["name"] = headline # Add description (singular) to link_data if description: creative_data["object_story_spec"]["link_data"]["description"] = description # Add call_to_action to link_data for simple creatives if call_to_action_type: creative_data["object_story_spec"]["link_data"]["call_to_action"] = { "type": call_to_action_type } # Add dynamic creative spec if provided if dynamic_creative_spec: creative_data["dynamic_creative_spec"] = dynamic_creative_spec if instagram_actor_id: creative_data["instagram_actor_id"] = instagram_actor_id # Prepare the API endpoint for creating a creative endpoint = f"{account_id}/adcreatives" try: # Make API request to create the creative data = await make_api_request(endpoint, access_token, creative_data, method="POST") # If successful, get more details about the created creative if "id" in data: creative_id = data["id"] creative_endpoint = f"{creative_id}" creative_params = { "fields": "id,name,status,thumbnail_url,image_url,image_hash,object_story_spec,asset_feed_spec,url_tags,link_url" } creative_details = await make_api_request(creative_endpoint, access_token, creative_params) return json.dumps({ "success": True, "creative_id": creative_id, "details": creative_details }, indent=2) return json.dumps(data, indent=2) except Exception as e: return json.dumps({ "error": "Failed to create ad creative", "details": str(e), "creative_data_sent": creative_data }, indent=2)
  • The @mcp_server.tool() decorator registers the create_ad_creative function as an MCP tool.
    @mcp_server.tool()
  • Function signature and docstring define the input schema/parameters for the tool, including validation logic within the function.
    async def create_ad_creative( account_id: str, image_hash: str, access_token: Optional[str] = None, name: Optional[str] = None, page_id: Optional[str] = None, link_url: Optional[str] = None, message: Optional[str] = None, headline: Optional[str] = None, headlines: Optional[List[str]] = None, description: Optional[str] = None, descriptions: Optional[List[str]] = None, dynamic_creative_spec: Optional[Dict[str, Any]] = None, call_to_action_type: Optional[str] = None, instagram_actor_id: Optional[str] = None ) -> str:

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/pipeboard-co/meta-ads-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server