meta-ads-mcp 0.11.1__tar.gz → 0.11.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. {meta_ads_mcp-0.11.1 → meta_ads_mcp-0.11.3}/PKG-INFO +18 -18
  2. {meta_ads_mcp-0.11.1 → meta_ads_mcp-0.11.3}/README.md +16 -16
  3. {meta_ads_mcp-0.11.1 → meta_ads_mcp-0.11.3}/meta_ads_mcp/__init__.py +1 -1
  4. {meta_ads_mcp-0.11.1 → meta_ads_mcp-0.11.3}/meta_ads_mcp/core/ads.py +4 -0
  5. {meta_ads_mcp-0.11.1 → meta_ads_mcp-0.11.3}/meta_ads_mcp/core/adsets.py +16 -5
  6. {meta_ads_mcp-0.11.1 → meta_ads_mcp-0.11.3}/pyproject.toml +2 -2
  7. {meta_ads_mcp-0.11.1 → meta_ads_mcp-0.11.3}/requirements.txt +1 -1
  8. meta_ads_mcp-0.11.3/tests/test_is_dynamic_creative_adset.py +77 -0
  9. {meta_ads_mcp-0.11.1 → meta_ads_mcp-0.11.3}/.github/workflows/publish.yml +0 -0
  10. {meta_ads_mcp-0.11.1 → meta_ads_mcp-0.11.3}/.github/workflows/test.yml +0 -0
  11. {meta_ads_mcp-0.11.1 → meta_ads_mcp-0.11.3}/.gitignore +0 -0
  12. {meta_ads_mcp-0.11.1 → meta_ads_mcp-0.11.3}/CUSTOM_META_APP.md +0 -0
  13. {meta_ads_mcp-0.11.1 → meta_ads_mcp-0.11.3}/Dockerfile +0 -0
  14. {meta_ads_mcp-0.11.1 → meta_ads_mcp-0.11.3}/LICENSE +0 -0
  15. {meta_ads_mcp-0.11.1 → meta_ads_mcp-0.11.3}/LOCAL_INSTALLATION.md +0 -0
  16. {meta_ads_mcp-0.11.1 → meta_ads_mcp-0.11.3}/META_API_NOTES.md +0 -0
  17. {meta_ads_mcp-0.11.1 → meta_ads_mcp-0.11.3}/RELEASE.md +0 -0
  18. {meta_ads_mcp-0.11.1 → meta_ads_mcp-0.11.3}/STREAMABLE_HTTP_SETUP.md +0 -0
  19. {meta_ads_mcp-0.11.1 → meta_ads_mcp-0.11.3}/examples/README.md +0 -0
  20. {meta_ads_mcp-0.11.1 → meta_ads_mcp-0.11.3}/examples/example_http_client.py +0 -0
  21. {meta_ads_mcp-0.11.1 → meta_ads_mcp-0.11.3}/future_improvements.md +0 -0
  22. {meta_ads_mcp-0.11.1 → meta_ads_mcp-0.11.3}/images/meta-ads-example.png +0 -0
  23. {meta_ads_mcp-0.11.1 → meta_ads_mcp-0.11.3}/meta_ads_auth.sh +0 -0
  24. {meta_ads_mcp-0.11.1 → meta_ads_mcp-0.11.3}/meta_ads_mcp/__main__.py +0 -0
  25. {meta_ads_mcp-0.11.1 → meta_ads_mcp-0.11.3}/meta_ads_mcp/core/__init__.py +0 -0
  26. {meta_ads_mcp-0.11.1 → meta_ads_mcp-0.11.3}/meta_ads_mcp/core/accounts.py +0 -0
  27. {meta_ads_mcp-0.11.1 → meta_ads_mcp-0.11.3}/meta_ads_mcp/core/ads_library.py +0 -0
  28. {meta_ads_mcp-0.11.1 → meta_ads_mcp-0.11.3}/meta_ads_mcp/core/api.py +0 -0
  29. {meta_ads_mcp-0.11.1 → meta_ads_mcp-0.11.3}/meta_ads_mcp/core/auth.py +0 -0
  30. {meta_ads_mcp-0.11.1 → meta_ads_mcp-0.11.3}/meta_ads_mcp/core/authentication.py +0 -0
  31. {meta_ads_mcp-0.11.1 → meta_ads_mcp-0.11.3}/meta_ads_mcp/core/budget_schedules.py +0 -0
  32. {meta_ads_mcp-0.11.1 → meta_ads_mcp-0.11.3}/meta_ads_mcp/core/callback_server.py +0 -0
  33. {meta_ads_mcp-0.11.1 → meta_ads_mcp-0.11.3}/meta_ads_mcp/core/campaigns.py +0 -0
  34. {meta_ads_mcp-0.11.1 → meta_ads_mcp-0.11.3}/meta_ads_mcp/core/duplication.py +0 -0
  35. {meta_ads_mcp-0.11.1 → meta_ads_mcp-0.11.3}/meta_ads_mcp/core/http_auth_integration.py +0 -0
  36. {meta_ads_mcp-0.11.1 → meta_ads_mcp-0.11.3}/meta_ads_mcp/core/insights.py +0 -0
  37. {meta_ads_mcp-0.11.1 → meta_ads_mcp-0.11.3}/meta_ads_mcp/core/openai_deep_research.py +0 -0
  38. {meta_ads_mcp-0.11.1 → meta_ads_mcp-0.11.3}/meta_ads_mcp/core/pipeboard_auth.py +0 -0
  39. {meta_ads_mcp-0.11.1 → meta_ads_mcp-0.11.3}/meta_ads_mcp/core/reports.py +0 -0
  40. {meta_ads_mcp-0.11.1 → meta_ads_mcp-0.11.3}/meta_ads_mcp/core/resources.py +0 -0
  41. {meta_ads_mcp-0.11.1 → meta_ads_mcp-0.11.3}/meta_ads_mcp/core/server.py +0 -0
  42. {meta_ads_mcp-0.11.1 → meta_ads_mcp-0.11.3}/meta_ads_mcp/core/targeting.py +0 -0
  43. {meta_ads_mcp-0.11.1 → meta_ads_mcp-0.11.3}/meta_ads_mcp/core/utils.py +0 -0
  44. {meta_ads_mcp-0.11.1 → meta_ads_mcp-0.11.3}/setup.py +0 -0
  45. {meta_ads_mcp-0.11.1 → meta_ads_mcp-0.11.3}/smithery.yaml +0 -0
  46. {meta_ads_mcp-0.11.1 → meta_ads_mcp-0.11.3}/tests/README.md +0 -0
  47. {meta_ads_mcp-0.11.1 → meta_ads_mcp-0.11.3}/tests/README_REGRESSION_TESTS.md +0 -0
  48. {meta_ads_mcp-0.11.1 → meta_ads_mcp-0.11.3}/tests/__init__.py +0 -0
  49. {meta_ads_mcp-0.11.1 → meta_ads_mcp-0.11.3}/tests/conftest.py +0 -0
  50. {meta_ads_mcp-0.11.1 → meta_ads_mcp-0.11.3}/tests/e2e_account_info_search_issue.py +0 -0
  51. {meta_ads_mcp-0.11.1 → meta_ads_mcp-0.11.3}/tests/test_account_info_access_fix.py +0 -0
  52. {meta_ads_mcp-0.11.1 → meta_ads_mcp-0.11.3}/tests/test_account_search.py +0 -0
  53. {meta_ads_mcp-0.11.1 → meta_ads_mcp-0.11.3}/tests/test_budget_update.py +0 -0
  54. {meta_ads_mcp-0.11.1 → meta_ads_mcp-0.11.3}/tests/test_budget_update_e2e.py +0 -0
  55. {meta_ads_mcp-0.11.1 → meta_ads_mcp-0.11.3}/tests/test_dsa_beneficiary.py +0 -0
  56. {meta_ads_mcp-0.11.1 → meta_ads_mcp-0.11.3}/tests/test_dsa_integration.py +0 -0
  57. {meta_ads_mcp-0.11.1 → meta_ads_mcp-0.11.3}/tests/test_duplication.py +0 -0
  58. {meta_ads_mcp-0.11.1 → meta_ads_mcp-0.11.3}/tests/test_duplication_regression.py +0 -0
  59. {meta_ads_mcp-0.11.1 → meta_ads_mcp-0.11.3}/tests/test_dynamic_creatives.py +0 -0
  60. {meta_ads_mcp-0.11.1 → meta_ads_mcp-0.11.3}/tests/test_estimate_audience_size.py +0 -0
  61. {meta_ads_mcp-0.11.1 → meta_ads_mcp-0.11.3}/tests/test_estimate_audience_size_e2e.py +0 -0
  62. {meta_ads_mcp-0.11.1 → meta_ads_mcp-0.11.3}/tests/test_get_account_pages.py +0 -0
  63. {meta_ads_mcp-0.11.1 → meta_ads_mcp-0.11.3}/tests/test_get_ad_creatives_fix.py +0 -0
  64. {meta_ads_mcp-0.11.1 → meta_ads_mcp-0.11.3}/tests/test_get_ad_image_quality_improvements.py +0 -0
  65. {meta_ads_mcp-0.11.1 → meta_ads_mcp-0.11.3}/tests/test_get_ad_image_regression.py +0 -0
  66. {meta_ads_mcp-0.11.1 → meta_ads_mcp-0.11.3}/tests/test_http_transport.py +0 -0
  67. {meta_ads_mcp-0.11.1 → meta_ads_mcp-0.11.3}/tests/test_insights_actions_and_values.py +0 -0
  68. {meta_ads_mcp-0.11.1 → meta_ads_mcp-0.11.3}/tests/test_integration_openai_mcp.py +0 -0
  69. {meta_ads_mcp-0.11.1 → meta_ads_mcp-0.11.3}/tests/test_mobile_app_adset_creation.py +0 -0
  70. {meta_ads_mcp-0.11.1 → meta_ads_mcp-0.11.3}/tests/test_mobile_app_adset_issue.py +0 -0
  71. {meta_ads_mcp-0.11.1 → meta_ads_mcp-0.11.3}/tests/test_openai.py +0 -0
  72. {meta_ads_mcp-0.11.1 → meta_ads_mcp-0.11.3}/tests/test_openai_mcp_deep_research.py +0 -0
  73. {meta_ads_mcp-0.11.1 → meta_ads_mcp-0.11.3}/tests/test_page_discovery.py +0 -0
  74. {meta_ads_mcp-0.11.1 → meta_ads_mcp-0.11.3}/tests/test_page_discovery_integration.py +0 -0
  75. {meta_ads_mcp-0.11.1 → meta_ads_mcp-0.11.3}/tests/test_targeting.py +0 -0
  76. {meta_ads_mcp-0.11.1 → meta_ads_mcp-0.11.3}/tests/test_targeting_search_e2e.py +0 -0
  77. {meta_ads_mcp-0.11.1 → meta_ads_mcp-0.11.3}/tests/test_update_ad_creative_id.py +0 -0
  78. {meta_ads_mcp-0.11.1 → meta_ads_mcp-0.11.3}/tests/test_upload_ad_image.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: meta-ads-mcp
3
- Version: 0.11.1
3
+ Version: 0.11.3
4
4
  Summary: Model Context Protocol (MCP) server for interacting with Meta Ads API
5
5
  Project-URL: Homepage, https://github.com/pipeboard-co/meta-ads-mcp
6
6
  Project-URL: Bug Tracker, https://github.com/pipeboard-co/meta-ads-mcp/issues
@@ -13,7 +13,7 @@ Classifier: Operating System :: OS Independent
13
13
  Classifier: Programming Language :: Python :: 3
14
14
  Requires-Python: >=3.10
15
15
  Requires-Dist: httpx>=0.26.0
16
- Requires-Dist: mcp[cli]<=1.12.2,>=1.10.1
16
+ Requires-Dist: mcp[cli]==1.12.2
17
17
  Requires-Dist: pathlib>=1.0.1
18
18
  Requires-Dist: pillow>=10.0.0
19
19
  Requires-Dist: pytest-asyncio>=1.0.0
@@ -371,16 +371,16 @@ For local installation configuration, authentication options, and advanced techn
371
371
  - `access_token` (optional): Meta API access token (will use cached token if not provided)
372
372
  - Returns: A clickable resource link for Meta authentication
373
373
 
374
- 22. `mcp_meta-ads_create_budget_schedule`
375
- - Create a budget schedule for a Meta Ads campaign.
374
+ 22. `mcp_meta_ads_create_budget_schedule`
375
+ - Create a budget schedule for a Meta Ads campaign
376
376
  - Inputs:
377
- - `campaign_id`: Meta Ads campaign ID.
378
- - `budget_value`: Amount of budget increase.
379
- - `budget_value_type`: Type of budget value ("ABSOLUTE" or "MULTIPLIER").
380
- - `time_start`: Unix timestamp for when the high demand period should start.
381
- - `time_end`: Unix timestamp for when the high demand period should end.
382
- - `access_token` (optional): Meta API access token.
383
- - Returns: JSON string with the ID of the created budget schedule or an error message.
377
+ - `campaign_id`: Meta Ads campaign ID
378
+ - `budget_value`: Amount of budget increase
379
+ - `budget_value_type`: Type of budget value ("ABSOLUTE" or "MULTIPLIER")
380
+ - `time_start`: Unix timestamp for when the high demand period should start
381
+ - `time_end`: Unix timestamp for when the high demand period should end
382
+ - `access_token` (optional): Meta API access token
383
+ - Returns: JSON string with the ID of the created budget schedule or an error message
384
384
 
385
385
  23. `mcp_meta_ads_search_interests`
386
386
  - Search for interest targeting options by keyword
@@ -398,7 +398,7 @@ For local installation configuration, authentication options, and advanced techn
398
398
  - `limit`: Maximum number of suggestions to return (default: 25)
399
399
  - Returns: Suggested interests with id, name, audience_size, and description fields
400
400
 
401
- 24. `mcp_meta_ads_validate_interests`
401
+ 25. `mcp_meta_ads_validate_interests`
402
402
  - Validate interest names or IDs for targeting
403
403
  - Inputs:
404
404
  - `access_token` (optional): Meta API access token (will use cached token if not provided)
@@ -406,14 +406,14 @@ For local installation configuration, authentication options, and advanced techn
406
406
  - `interest_fbid_list`: List of interest IDs to validate (e.g., ["6003700426513"])
407
407
  - Returns: Validation results showing valid status and audience_size for each interest
408
408
 
409
- 25. `mcp_meta_ads_search_behaviors`
409
+ 26. `mcp_meta_ads_search_behaviors`
410
410
  - Get all available behavior targeting options
411
411
  - Inputs:
412
412
  - `access_token` (optional): Meta API access token (will use cached token if not provided)
413
413
  - `limit`: Maximum number of results to return (default: 50)
414
414
  - Returns: Behavior targeting options with id, name, audience_size bounds, path, and description
415
415
 
416
- 26. `mcp_meta_ads_search_demographics`
416
+ 27. `mcp_meta_ads_search_demographics`
417
417
  - Get demographic targeting options
418
418
  - Inputs:
419
419
  - `access_token` (optional): Meta API access token (will use cached token if not provided)
@@ -421,7 +421,7 @@ For local installation configuration, authentication options, and advanced techn
421
421
  - `limit`: Maximum number of results to return (default: 50)
422
422
  - Returns: Demographic targeting options with id, name, audience_size bounds, path, and description
423
423
 
424
- 27. `mcp_meta_ads_search_geo_locations`
424
+ 28. `mcp_meta_ads_search_geo_locations`
425
425
  - Search for geographic targeting locations
426
426
  - Inputs:
427
427
  - `access_token` (optional): Meta API access token (will use cached token if not provided)
@@ -430,7 +430,7 @@ For local installation configuration, authentication options, and advanced techn
430
430
  - `limit`: Maximum number of results to return (default: 25)
431
431
  - Returns: Location data with key, name, type, and geographic hierarchy information
432
432
 
433
- 28. `mcp_meta_ads_search` (Enhanced)
433
+ 29. `mcp_meta_ads_search` (Enhanced)
434
434
  - Generic search across accounts, campaigns, ads, and pages
435
435
  - Automatically includes page searching when query mentions "page" or "pages"
436
436
  - Inputs:
@@ -479,7 +479,7 @@ The easiest way to avoid any setup issues is to **[🎯 use our Remote MCP inste
479
479
  For comprehensive troubleshooting, debugging, and local installation issues, see our **[Local Installation Guide](LOCAL_INSTALLATION.md)** which includes:
480
480
 
481
481
  - Authentication troubleshooting
482
- - Installation issues and solutions
482
+ - Installation issues and solutions
483
483
  - API error resolution
484
484
  - Debug logs and diagnostic commands
485
- - Performance optimization tips
485
+ - Performance optimization tips
@@ -346,16 +346,16 @@ For local installation configuration, authentication options, and advanced techn
346
346
  - `access_token` (optional): Meta API access token (will use cached token if not provided)
347
347
  - Returns: A clickable resource link for Meta authentication
348
348
 
349
- 22. `mcp_meta-ads_create_budget_schedule`
350
- - Create a budget schedule for a Meta Ads campaign.
349
+ 22. `mcp_meta_ads_create_budget_schedule`
350
+ - Create a budget schedule for a Meta Ads campaign
351
351
  - Inputs:
352
- - `campaign_id`: Meta Ads campaign ID.
353
- - `budget_value`: Amount of budget increase.
354
- - `budget_value_type`: Type of budget value ("ABSOLUTE" or "MULTIPLIER").
355
- - `time_start`: Unix timestamp for when the high demand period should start.
356
- - `time_end`: Unix timestamp for when the high demand period should end.
357
- - `access_token` (optional): Meta API access token.
358
- - Returns: JSON string with the ID of the created budget schedule or an error message.
352
+ - `campaign_id`: Meta Ads campaign ID
353
+ - `budget_value`: Amount of budget increase
354
+ - `budget_value_type`: Type of budget value ("ABSOLUTE" or "MULTIPLIER")
355
+ - `time_start`: Unix timestamp for when the high demand period should start
356
+ - `time_end`: Unix timestamp for when the high demand period should end
357
+ - `access_token` (optional): Meta API access token
358
+ - Returns: JSON string with the ID of the created budget schedule or an error message
359
359
 
360
360
  23. `mcp_meta_ads_search_interests`
361
361
  - Search for interest targeting options by keyword
@@ -373,7 +373,7 @@ For local installation configuration, authentication options, and advanced techn
373
373
  - `limit`: Maximum number of suggestions to return (default: 25)
374
374
  - Returns: Suggested interests with id, name, audience_size, and description fields
375
375
 
376
- 24. `mcp_meta_ads_validate_interests`
376
+ 25. `mcp_meta_ads_validate_interests`
377
377
  - Validate interest names or IDs for targeting
378
378
  - Inputs:
379
379
  - `access_token` (optional): Meta API access token (will use cached token if not provided)
@@ -381,14 +381,14 @@ For local installation configuration, authentication options, and advanced techn
381
381
  - `interest_fbid_list`: List of interest IDs to validate (e.g., ["6003700426513"])
382
382
  - Returns: Validation results showing valid status and audience_size for each interest
383
383
 
384
- 25. `mcp_meta_ads_search_behaviors`
384
+ 26. `mcp_meta_ads_search_behaviors`
385
385
  - Get all available behavior targeting options
386
386
  - Inputs:
387
387
  - `access_token` (optional): Meta API access token (will use cached token if not provided)
388
388
  - `limit`: Maximum number of results to return (default: 50)
389
389
  - Returns: Behavior targeting options with id, name, audience_size bounds, path, and description
390
390
 
391
- 26. `mcp_meta_ads_search_demographics`
391
+ 27. `mcp_meta_ads_search_demographics`
392
392
  - Get demographic targeting options
393
393
  - Inputs:
394
394
  - `access_token` (optional): Meta API access token (will use cached token if not provided)
@@ -396,7 +396,7 @@ For local installation configuration, authentication options, and advanced techn
396
396
  - `limit`: Maximum number of results to return (default: 50)
397
397
  - Returns: Demographic targeting options with id, name, audience_size bounds, path, and description
398
398
 
399
- 27. `mcp_meta_ads_search_geo_locations`
399
+ 28. `mcp_meta_ads_search_geo_locations`
400
400
  - Search for geographic targeting locations
401
401
  - Inputs:
402
402
  - `access_token` (optional): Meta API access token (will use cached token if not provided)
@@ -405,7 +405,7 @@ For local installation configuration, authentication options, and advanced techn
405
405
  - `limit`: Maximum number of results to return (default: 25)
406
406
  - Returns: Location data with key, name, type, and geographic hierarchy information
407
407
 
408
- 28. `mcp_meta_ads_search` (Enhanced)
408
+ 29. `mcp_meta_ads_search` (Enhanced)
409
409
  - Generic search across accounts, campaigns, ads, and pages
410
410
  - Automatically includes page searching when query mentions "page" or "pages"
411
411
  - Inputs:
@@ -454,7 +454,7 @@ The easiest way to avoid any setup issues is to **[🎯 use our Remote MCP inste
454
454
  For comprehensive troubleshooting, debugging, and local installation issues, see our **[Local Installation Guide](LOCAL_INSTALLATION.md)** which includes:
455
455
 
456
456
  - Authentication troubleshooting
457
- - Installation issues and solutions
457
+ - Installation issues and solutions
458
458
  - API error resolution
459
459
  - Debug logs and diagnostic commands
460
- - Performance optimization tips
460
+ - Performance optimization tips
@@ -6,7 +6,7 @@ This package provides a Meta Ads MCP integration
6
6
 
7
7
  from meta_ads_mcp.core.server import main
8
8
 
9
- __version__ = "0.11.1"
9
+ __version__ = "0.11.3"
10
10
 
11
11
  __all__ = [
12
12
  'get_ad_accounts',
@@ -111,6 +111,10 @@ async def create_ad(
111
111
  tracking_specs: Optional tracking specifications (e.g., for pixel events).
112
112
  Example: [{"action.type":"offsite_conversion","fb_pixel":["YOUR_PIXEL_ID"]}]
113
113
  access_token: Meta API access token (optional - will use cached token if not provided)
114
+
115
+ Note:
116
+ Dynamic Creative creatives require the parent ad set to have `is_dynamic_creative=true`.
117
+ Otherwise, ad creation will fail with error_subcode 1885998.
114
118
  """
115
119
  # Check required parameters
116
120
  if not account_id:
@@ -27,14 +27,14 @@ async def get_adsets(account_id: str, access_token: Optional[str] = None, limit:
27
27
  if campaign_id:
28
28
  endpoint = f"{campaign_id}/adsets"
29
29
  params = {
30
- "fields": "id,name,campaign_id,status,daily_budget,lifetime_budget,targeting,bid_amount,bid_strategy,optimization_goal,billing_event,start_time,end_time,created_time,updated_time,frequency_control_specs{event,interval_days,max_frequency}",
30
+ "fields": "id,name,campaign_id,status,daily_budget,lifetime_budget,targeting,bid_amount,bid_strategy,optimization_goal,billing_event,start_time,end_time,created_time,updated_time,is_dynamic_creative,frequency_control_specs{event,interval_days,max_frequency}",
31
31
  "limit": limit
32
32
  }
33
33
  else:
34
34
  # Use account endpoint if no campaign_id is given
35
35
  endpoint = f"{account_id}/adsets"
36
36
  params = {
37
- "fields": "id,name,campaign_id,status,daily_budget,lifetime_budget,targeting,bid_amount,bid_strategy,optimization_goal,billing_event,start_time,end_time,created_time,updated_time,frequency_control_specs{event,interval_days,max_frequency}",
37
+ "fields": "id,name,campaign_id,status,daily_budget,lifetime_budget,targeting,bid_amount,bid_strategy,optimization_goal,billing_event,start_time,end_time,created_time,updated_time,is_dynamic_creative,frequency_control_specs{event,interval_days,max_frequency}",
38
38
  "limit": limit
39
39
  }
40
40
  # Note: Removed the attempt to add campaign_id to params for the account endpoint case,
@@ -67,7 +67,7 @@ async def get_adset_details(adset_id: str, access_token: Optional[str] = None) -
67
67
  endpoint = f"{adset_id}"
68
68
  # Explicitly prioritize frequency_control_specs in the fields request
69
69
  params = {
70
- "fields": "id,name,campaign_id,status,frequency_control_specs{event,interval_days,max_frequency},daily_budget,lifetime_budget,targeting,bid_amount,bid_strategy,optimization_goal,billing_event,start_time,end_time,created_time,updated_time,attribution_spec,destination_type,promoted_object,pacing_type,budget_remaining,dsa_beneficiary"
70
+ "fields": "id,name,campaign_id,status,frequency_control_specs{event,interval_days,max_frequency},daily_budget,lifetime_budget,targeting,bid_amount,bid_strategy,optimization_goal,billing_event,start_time,end_time,created_time,updated_time,attribution_spec,destination_type,promoted_object,pacing_type,budget_remaining,dsa_beneficiary,is_dynamic_creative"
71
71
  }
72
72
 
73
73
  data = await make_api_request(endpoint, access_token, params)
@@ -100,6 +100,7 @@ async def create_adset(
100
100
  dsa_beneficiary: Optional[str] = None,
101
101
  promoted_object: Optional[Dict[str, Any]] = None,
102
102
  destination_type: Optional[str] = None,
103
+ is_dynamic_creative: Optional[bool] = None,
103
104
  access_token: Optional[str] = None
104
105
  ) -> str:
105
106
  """
@@ -125,8 +126,9 @@ async def create_adset(
125
126
  Optional fields: custom_event_type, pixel_id, page_id.
126
127
  Example: {"application_id": "123456789012345", "object_store_url": "https://apps.apple.com/app/id123456789"}
127
128
  destination_type: Where users are directed after clicking the ad (e.g., 'APP_STORE', 'DEEPLINK', 'APP_INSTALL', 'ON_AD').
128
- Required for mobile app campaigns and lead generation campaigns.
129
- Use 'ON_AD' for lead generation campaigns where user interaction happens within the ad.
129
+ Required for mobile app campaigns and lead generation campaigns.
130
+ Use 'ON_AD' for lead generation campaigns where user interaction happens within the ad.
131
+ is_dynamic_creative: Enable Dynamic Creative for this ad set (required when using dynamic creatives with asset_feed_spec/dynamic_creative_spec).
130
132
  access_token: Meta API access token (optional - will use cached token if not provided)
131
133
  """
132
134
  # Check required parameters
@@ -249,6 +251,10 @@ async def create_adset(
249
251
  if destination_type:
250
252
  params["destination_type"] = destination_type
251
253
 
254
+ # Enable Dynamic Creative if requested
255
+ if is_dynamic_creative is not None:
256
+ params["is_dynamic_creative"] = "true" if bool(is_dynamic_creative) else "false"
257
+
252
258
  try:
253
259
  data = await make_api_request(endpoint, access_token, params, method="POST")
254
260
  return json.dumps(data, indent=2)
@@ -290,6 +296,7 @@ async def create_adset(
290
296
  async def update_adset(adset_id: str, frequency_control_specs: Optional[List[Dict[str, Any]]] = None, bid_strategy: Optional[str] = None,
291
297
  bid_amount: Optional[int] = None, status: Optional[str] = None, targeting: Optional[Dict[str, Any]] = None,
292
298
  optimization_goal: Optional[str] = None, daily_budget: Optional[int] = None, lifetime_budget: Optional[int] = None,
299
+ is_dynamic_creative: Optional[bool] = None,
293
300
  access_token: Optional[str] = None) -> str:
294
301
  """
295
302
  Update an ad set with new settings including frequency caps and budgets.
@@ -306,6 +313,7 @@ async def update_adset(adset_id: str, frequency_control_specs: Optional[List[Dic
306
313
  optimization_goal: Conversion optimization goal (e.g., 'LINK_CLICKS', 'CONVERSIONS', 'APP_INSTALLS', etc.)
307
314
  daily_budget: Daily budget in account currency (in cents) as a string
308
315
  lifetime_budget: Lifetime budget in account currency (in cents) as a string
316
+ is_dynamic_creative: Enable/disable Dynamic Creative for this ad set.
309
317
  access_token: Meta API access token (optional - will use cached token if not provided)
310
318
  """
311
319
  if not adset_id:
@@ -342,6 +350,9 @@ async def update_adset(adset_id: str, frequency_control_specs: Optional[List[Dic
342
350
  if lifetime_budget is not None:
343
351
  params['lifetime_budget'] = str(lifetime_budget)
344
352
 
353
+ if is_dynamic_creative is not None:
354
+ params['is_dynamic_creative'] = "true" if bool(is_dynamic_creative) else "false"
355
+
345
356
  if not params:
346
357
  return json.dumps({"error": "No update parameters provided"}, indent=2)
347
358
 
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "meta-ads-mcp"
7
- version = "0.11.1"
7
+ version = "0.11.3"
8
8
  description = "Model Context Protocol (MCP) server for interacting with Meta Ads API"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -20,7 +20,7 @@ classifiers = [
20
20
  ]
21
21
  dependencies = [
22
22
  "httpx>=0.26.0",
23
- "mcp[cli]>=1.10.1,<=1.12.2",
23
+ "mcp[cli]==1.12.2",
24
24
  "python-dotenv>=1.1.0",
25
25
  "requests>=2.32.3",
26
26
  "Pillow>=10.0.0",
@@ -1,5 +1,5 @@
1
1
  httpx>=0.26.0
2
- mcp[cli]>=1.10.1
2
+ mcp[cli]==1.12.2
3
3
  python-dotenv>=1.1.0
4
4
  requests>=2.32.3
5
5
  Pillow>=10.0.0
@@ -0,0 +1,77 @@
1
+ import json
2
+ import pytest
3
+ from unittest.mock import AsyncMock, patch
4
+
5
+ from meta_ads_mcp.core.adsets import create_adset, update_adset, get_adsets, get_adset_details
6
+
7
+
8
+ @pytest.mark.asyncio
9
+ async def test_create_adset_includes_is_dynamic_creative_true():
10
+ sample_response = {"id": "adset_1", "name": "DC Adset"}
11
+ with patch('meta_ads_mcp.core.adsets.make_api_request', new_callable=AsyncMock) as mock_api:
12
+ mock_api.return_value = sample_response
13
+
14
+ result = await create_adset(
15
+ account_id="act_123",
16
+ campaign_id="cmp_1",
17
+ name="DC Adset",
18
+ optimization_goal="LINK_CLICKS",
19
+ billing_event="IMPRESSIONS",
20
+ targeting={"geo_locations": {"countries": ["US"]}},
21
+ is_dynamic_creative=True,
22
+ access_token="test_token",
23
+ )
24
+
25
+ assert json.loads(result)["id"] == "adset_1"
26
+ # Verify param was sent as string boolean
27
+ call_args = mock_api.call_args
28
+ params = call_args[0][2]
29
+ assert params["is_dynamic_creative"] == "true"
30
+
31
+
32
+ @pytest.mark.asyncio
33
+ async def test_update_adset_includes_is_dynamic_creative_false():
34
+ sample_response = {"success": True}
35
+ with patch('meta_ads_mcp.core.adsets.make_api_request', new_callable=AsyncMock) as mock_api:
36
+ mock_api.return_value = sample_response
37
+
38
+ result = await update_adset(
39
+ adset_id="120",
40
+ is_dynamic_creative=False,
41
+ access_token="test_token",
42
+ )
43
+
44
+ assert json.loads(result)["success"] is True
45
+ call_args = mock_api.call_args
46
+ params = call_args[0][2]
47
+ assert params["is_dynamic_creative"] == "false"
48
+
49
+
50
+ @pytest.mark.asyncio
51
+ async def test_get_adsets_fields_include_is_dynamic_creative():
52
+ sample_response = {"data": []}
53
+ with patch('meta_ads_mcp.core.adsets.make_api_request', new_callable=AsyncMock) as mock_api:
54
+ mock_api.return_value = sample_response
55
+
56
+ result = await get_adsets(account_id="act_123", access_token="test_token", limit=1)
57
+ assert json.loads(result)["data"] == []
58
+
59
+ call_args = mock_api.call_args
60
+ params = call_args[0][2]
61
+ assert "is_dynamic_creative" in params.get("fields", "")
62
+
63
+
64
+ @pytest.mark.asyncio
65
+ async def test_get_adset_details_fields_include_is_dynamic_creative():
66
+ sample_response = {"id": "120", "name": "Test", "is_dynamic_creative": True}
67
+ with patch('meta_ads_mcp.core.adsets.make_api_request', new_callable=AsyncMock) as mock_api:
68
+ mock_api.return_value = sample_response
69
+
70
+ result = await get_adset_details(adset_id="120", access_token="test_token")
71
+ assert json.loads(result)["id"] == "120"
72
+
73
+ call_args = mock_api.call_args
74
+ params = call_args[0][2]
75
+ assert "is_dynamic_creative" in params.get("fields", "")
76
+
77
+
File without changes
File without changes
File without changes
File without changes
File without changes