clone_ad_set
Duplicate Meta ad sets to replicate targeting, budget, and creative configurations for testing or scaling campaigns.
Instructions
Duplicate an ad set using Meta's local Graph copy edge.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| ad_set_id | Yes | ||
| meta_access_token | No | ||
| target_campaign_id | No | ||
| name_suffix | No | - Copy | |
| include_ads | No | ||
| include_creatives | No | ||
| new_daily_budget | No | ||
| new_targeting | No | ||
| new_status | No | PAUSED |
Implementation Reference
- The tool handler function `clone_ad_set` which implements the logic for duplicating an ad set by calling `_forward_duplication_request`.
async def clone_ad_set( ad_set_id: str, meta_access_token: Optional[str] = None, target_campaign_id: Optional[str] = None, name_suffix: Optional[str] = " - Copy", include_ads: bool = True, include_creatives: bool = True, new_daily_budget: Optional[float] = None, new_targeting: Optional[Dict[str, Any]] = None, new_status: Optional[str] = "PAUSED", ) -> str: """Duplicate an ad set using Meta's local Graph copy edge.""" return await _forward_duplication_request( "adset", ad_set_id, meta_access_token, { "target_campaign_id": target_campaign_id, "name_suffix": name_suffix, "include_ads": include_ads, "include_creatives": include_creatives, "new_daily_budget": new_daily_budget, "new_targeting": new_targeting, "new_status": new_status, }, ) - src/armavita_meta_ads_mcp/core/duplication_tools.py:366-396 (registration)Registration of `clone_ad_set` as an MCP tool using `@mcp_server.tool()` and `@meta_api_tool`.
@mcp_server.tool() @meta_api_tool async def clone_ad_set( ad_set_id: str, meta_access_token: Optional[str] = None, target_campaign_id: Optional[str] = None, name_suffix: Optional[str] = " - Copy", include_ads: bool = True, include_creatives: bool = True, new_daily_budget: Optional[float] = None, new_targeting: Optional[Dict[str, Any]] = None, new_status: Optional[str] = "PAUSED", ) -> str: """Duplicate an ad set using Meta's local Graph copy edge.""" return await _forward_duplication_request( "adset", ad_set_id, meta_access_token, { "target_campaign_id": target_campaign_id, "name_suffix": name_suffix, "include_ads": include_ads, "include_creatives": include_creatives, "new_daily_budget": new_daily_budget, "new_targeting": new_targeting, "new_status": new_status, }, ) @mcp_server.tool() - Helper function that performs the actual API request to the Meta Graph API copy edge for all duplication tools.
async def _forward_duplication_request( resource_type: str, resource_id: str, meta_access_token: Optional[str], options: Dict[str, Any], ) -> str: """Execute OSS-local duplication against Graph API copy edges.""" try: facebook_token = meta_access_token if meta_access_token else await auth.get_current_access_token() if not facebook_token: raise DuplicationError( json.dumps( { "success": False, "error": "authentication_required", "message": "Meta Ads access token not found", "details": { "required": "Valid Meta access token", "check": "Authenticate and retry duplication request.", }, }, indent=2, ) ) preflight_block = await _run_v25_duplication_preflight(resource_type, resource_id, facebook_token) if preflight_block: raise DuplicationError( json.dumps( { "success": False, "error": "v25_blocked_operation", "message": "Duplication is blocked for deprecated Advantage+ Shopping/App campaign flows in v25.", "details": { "resource_type": resource_type, "resource_id": resource_id, "campaign_id": preflight_block.get("campaign_id"), "campaign_name": preflight_block.get("campaign_name"), "campaign_objective": preflight_block.get("campaign_objective"), "smart_promotion_type": preflight_block.get("smart_promotion_type"), "reason": preflight_block.get("reason"), }, "suggestion": ( "Use supported Advantage+ migration or rebuild the campaign with v25-compatible " "flows before attempting duplication." ), }, indent=2, ) ) copy_params, warnings = _build_copy_params(resource_type, options) endpoint = f"{resource_id}/copies" data = await make_api_request(endpoint, facebook_token, copy_params, method="POST") if not isinstance(data, dict): raise DuplicationError( json.dumps( { "success": False, "error": "duplication_failed", "message": "Unexpected response from Graph API copy edge", "details": { "resource_type": resource_type, "resource_id": resource_id, "response_type": type(data).__name__, }, }, indent=2, ) ) graph_error = data.get("error") if isinstance(data.get("error"), dict) else None if graph_error: code = graph_error.get("code") if code == 4: raise RateLimitError( json.dumps( { "error": "rate_limit_exceeded", "message": graph_error.get("message") or graph_error.get("primary_text", "Meta API rate limit exceeded"), "details": { "code": code, "error_subcode": graph_error.get("error_subcode"), "retry_hint": "Retry with backoff.", }, }, indent=2, ) ) raise DuplicationError( json.dumps( _build_graph_error_payload(resource_type, resource_id, graph_error), indent=2, ) ) new_id = _extract_new_id(resource_type, data) success = bool(new_id) or bool(data.get("success") is True) return json.dumps( { "success": success, "source_id": resource_id, "resource_type": resource_type, "new_id": new_id, "warnings": warnings, "meta_response": data, }, indent=2, )