meta-ads-mcp 0.10.0__tar.gz → 0.10.1__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 (76) hide show
  1. meta_ads_mcp-0.10.0/README.md → meta_ads_mcp-0.10.1/PKG-INFO +47 -0
  2. meta_ads_mcp-0.10.0/PKG-INFO → meta_ads_mcp-0.10.1/README.md +22 -25
  3. {meta_ads_mcp-0.10.0 → meta_ads_mcp-0.10.1}/meta_ads_mcp/__init__.py +1 -1
  4. {meta_ads_mcp-0.10.0 → meta_ads_mcp-0.10.1}/meta_ads_mcp/core/targeting.py +65 -21
  5. {meta_ads_mcp-0.10.0 → meta_ads_mcp-0.10.1}/pyproject.toml +2 -2
  6. {meta_ads_mcp-0.10.0 → meta_ads_mcp-0.10.1}/tests/test_estimate_audience_size.py +15 -25
  7. {meta_ads_mcp-0.10.0 → meta_ads_mcp-0.10.1}/tests/test_estimate_audience_size_e2e.py +1 -1
  8. {meta_ads_mcp-0.10.0 → meta_ads_mcp-0.10.1}/.github/workflows/publish.yml +0 -0
  9. {meta_ads_mcp-0.10.0 → meta_ads_mcp-0.10.1}/.github/workflows/test.yml +0 -0
  10. {meta_ads_mcp-0.10.0 → meta_ads_mcp-0.10.1}/.gitignore +0 -0
  11. {meta_ads_mcp-0.10.0 → meta_ads_mcp-0.10.1}/CUSTOM_META_APP.md +0 -0
  12. {meta_ads_mcp-0.10.0 → meta_ads_mcp-0.10.1}/Dockerfile +0 -0
  13. {meta_ads_mcp-0.10.0 → meta_ads_mcp-0.10.1}/LICENSE +0 -0
  14. {meta_ads_mcp-0.10.0 → meta_ads_mcp-0.10.1}/LOCAL_INSTALLATION.md +0 -0
  15. {meta_ads_mcp-0.10.0 → meta_ads_mcp-0.10.1}/META_API_NOTES.md +0 -0
  16. {meta_ads_mcp-0.10.0 → meta_ads_mcp-0.10.1}/RELEASE.md +0 -0
  17. {meta_ads_mcp-0.10.0 → meta_ads_mcp-0.10.1}/STREAMABLE_HTTP_SETUP.md +0 -0
  18. {meta_ads_mcp-0.10.0 → meta_ads_mcp-0.10.1}/examples/README.md +0 -0
  19. {meta_ads_mcp-0.10.0 → meta_ads_mcp-0.10.1}/examples/example_http_client.py +0 -0
  20. {meta_ads_mcp-0.10.0 → meta_ads_mcp-0.10.1}/future_improvements.md +0 -0
  21. {meta_ads_mcp-0.10.0 → meta_ads_mcp-0.10.1}/images/meta-ads-example.png +0 -0
  22. {meta_ads_mcp-0.10.0 → meta_ads_mcp-0.10.1}/meta_ads_auth.sh +0 -0
  23. {meta_ads_mcp-0.10.0 → meta_ads_mcp-0.10.1}/meta_ads_mcp/__main__.py +0 -0
  24. {meta_ads_mcp-0.10.0 → meta_ads_mcp-0.10.1}/meta_ads_mcp/core/__init__.py +0 -0
  25. {meta_ads_mcp-0.10.0 → meta_ads_mcp-0.10.1}/meta_ads_mcp/core/accounts.py +0 -0
  26. {meta_ads_mcp-0.10.0 → meta_ads_mcp-0.10.1}/meta_ads_mcp/core/ads.py +0 -0
  27. {meta_ads_mcp-0.10.0 → meta_ads_mcp-0.10.1}/meta_ads_mcp/core/ads_library.py +0 -0
  28. {meta_ads_mcp-0.10.0 → meta_ads_mcp-0.10.1}/meta_ads_mcp/core/adsets.py +0 -0
  29. {meta_ads_mcp-0.10.0 → meta_ads_mcp-0.10.1}/meta_ads_mcp/core/api.py +0 -0
  30. {meta_ads_mcp-0.10.0 → meta_ads_mcp-0.10.1}/meta_ads_mcp/core/auth.py +0 -0
  31. {meta_ads_mcp-0.10.0 → meta_ads_mcp-0.10.1}/meta_ads_mcp/core/authentication.py +0 -0
  32. {meta_ads_mcp-0.10.0 → meta_ads_mcp-0.10.1}/meta_ads_mcp/core/budget_schedules.py +0 -0
  33. {meta_ads_mcp-0.10.0 → meta_ads_mcp-0.10.1}/meta_ads_mcp/core/callback_server.py +0 -0
  34. {meta_ads_mcp-0.10.0 → meta_ads_mcp-0.10.1}/meta_ads_mcp/core/campaigns.py +0 -0
  35. {meta_ads_mcp-0.10.0 → meta_ads_mcp-0.10.1}/meta_ads_mcp/core/duplication.py +0 -0
  36. {meta_ads_mcp-0.10.0 → meta_ads_mcp-0.10.1}/meta_ads_mcp/core/http_auth_integration.py +0 -0
  37. {meta_ads_mcp-0.10.0 → meta_ads_mcp-0.10.1}/meta_ads_mcp/core/insights.py +0 -0
  38. {meta_ads_mcp-0.10.0 → meta_ads_mcp-0.10.1}/meta_ads_mcp/core/openai_deep_research.py +0 -0
  39. {meta_ads_mcp-0.10.0 → meta_ads_mcp-0.10.1}/meta_ads_mcp/core/pipeboard_auth.py +0 -0
  40. {meta_ads_mcp-0.10.0 → meta_ads_mcp-0.10.1}/meta_ads_mcp/core/reports.py +0 -0
  41. {meta_ads_mcp-0.10.0 → meta_ads_mcp-0.10.1}/meta_ads_mcp/core/resources.py +0 -0
  42. {meta_ads_mcp-0.10.0 → meta_ads_mcp-0.10.1}/meta_ads_mcp/core/server.py +0 -0
  43. {meta_ads_mcp-0.10.0 → meta_ads_mcp-0.10.1}/meta_ads_mcp/core/utils.py +0 -0
  44. {meta_ads_mcp-0.10.0 → meta_ads_mcp-0.10.1}/requirements.txt +0 -0
  45. {meta_ads_mcp-0.10.0 → meta_ads_mcp-0.10.1}/setup.py +0 -0
  46. {meta_ads_mcp-0.10.0 → meta_ads_mcp-0.10.1}/smithery.yaml +0 -0
  47. {meta_ads_mcp-0.10.0 → meta_ads_mcp-0.10.1}/tests/README.md +0 -0
  48. {meta_ads_mcp-0.10.0 → meta_ads_mcp-0.10.1}/tests/README_REGRESSION_TESTS.md +0 -0
  49. {meta_ads_mcp-0.10.0 → meta_ads_mcp-0.10.1}/tests/__init__.py +0 -0
  50. {meta_ads_mcp-0.10.0 → meta_ads_mcp-0.10.1}/tests/conftest.py +0 -0
  51. {meta_ads_mcp-0.10.0 → meta_ads_mcp-0.10.1}/tests/e2e_account_info_search_issue.py +0 -0
  52. {meta_ads_mcp-0.10.0 → meta_ads_mcp-0.10.1}/tests/test_account_info_access_fix.py +0 -0
  53. {meta_ads_mcp-0.10.0 → meta_ads_mcp-0.10.1}/tests/test_account_search.py +0 -0
  54. {meta_ads_mcp-0.10.0 → meta_ads_mcp-0.10.1}/tests/test_budget_update.py +0 -0
  55. {meta_ads_mcp-0.10.0 → meta_ads_mcp-0.10.1}/tests/test_budget_update_e2e.py +0 -0
  56. {meta_ads_mcp-0.10.0 → meta_ads_mcp-0.10.1}/tests/test_dsa_beneficiary.py +0 -0
  57. {meta_ads_mcp-0.10.0 → meta_ads_mcp-0.10.1}/tests/test_dsa_integration.py +0 -0
  58. {meta_ads_mcp-0.10.0 → meta_ads_mcp-0.10.1}/tests/test_duplication.py +0 -0
  59. {meta_ads_mcp-0.10.0 → meta_ads_mcp-0.10.1}/tests/test_duplication_regression.py +0 -0
  60. {meta_ads_mcp-0.10.0 → meta_ads_mcp-0.10.1}/tests/test_dynamic_creatives.py +0 -0
  61. {meta_ads_mcp-0.10.0 → meta_ads_mcp-0.10.1}/tests/test_get_account_pages.py +0 -0
  62. {meta_ads_mcp-0.10.0 → meta_ads_mcp-0.10.1}/tests/test_get_ad_creatives_fix.py +0 -0
  63. {meta_ads_mcp-0.10.0 → meta_ads_mcp-0.10.1}/tests/test_get_ad_image_quality_improvements.py +0 -0
  64. {meta_ads_mcp-0.10.0 → meta_ads_mcp-0.10.1}/tests/test_get_ad_image_regression.py +0 -0
  65. {meta_ads_mcp-0.10.0 → meta_ads_mcp-0.10.1}/tests/test_http_transport.py +0 -0
  66. {meta_ads_mcp-0.10.0 → meta_ads_mcp-0.10.1}/tests/test_insights_actions_and_values.py +0 -0
  67. {meta_ads_mcp-0.10.0 → meta_ads_mcp-0.10.1}/tests/test_integration_openai_mcp.py +0 -0
  68. {meta_ads_mcp-0.10.0 → meta_ads_mcp-0.10.1}/tests/test_mobile_app_adset_creation.py +0 -0
  69. {meta_ads_mcp-0.10.0 → meta_ads_mcp-0.10.1}/tests/test_mobile_app_adset_issue.py +0 -0
  70. {meta_ads_mcp-0.10.0 → meta_ads_mcp-0.10.1}/tests/test_openai.py +0 -0
  71. {meta_ads_mcp-0.10.0 → meta_ads_mcp-0.10.1}/tests/test_openai_mcp_deep_research.py +0 -0
  72. {meta_ads_mcp-0.10.0 → meta_ads_mcp-0.10.1}/tests/test_page_discovery.py +0 -0
  73. {meta_ads_mcp-0.10.0 → meta_ads_mcp-0.10.1}/tests/test_page_discovery_integration.py +0 -0
  74. {meta_ads_mcp-0.10.0 → meta_ads_mcp-0.10.1}/tests/test_targeting.py +0 -0
  75. {meta_ads_mcp-0.10.0 → meta_ads_mcp-0.10.1}/tests/test_targeting_search_e2e.py +0 -0
  76. {meta_ads_mcp-0.10.0 → meta_ads_mcp-0.10.1}/tests/test_update_ad_creative_id.py +0 -0
@@ -1,3 +1,28 @@
1
+ Metadata-Version: 2.4
2
+ Name: meta-ads-mcp
3
+ Version: 0.10.1
4
+ Summary: Model Context Protocol (MCP) server for interacting with Meta Ads API
5
+ Project-URL: Homepage, https://github.com/pipeboard-co/meta-ads-mcp
6
+ Project-URL: Bug Tracker, https://github.com/pipeboard-co/meta-ads-mcp/issues
7
+ Author-email: Yves Junqueira <yves.junqueira@gmail.com>
8
+ License: BUSL-1.1
9
+ License-File: LICENSE
10
+ Keywords: ads,api,claude,facebook,mcp,meta
11
+ Classifier: License :: Other/Proprietary License
12
+ Classifier: Operating System :: OS Independent
13
+ Classifier: Programming Language :: Python :: 3
14
+ Requires-Python: >=3.10
15
+ Requires-Dist: httpx>=0.26.0
16
+ Requires-Dist: mcp[cli]<=1.12.2,>=1.10.1
17
+ Requires-Dist: pathlib>=1.0.1
18
+ Requires-Dist: pillow>=10.0.0
19
+ Requires-Dist: pytest-asyncio>=1.0.0
20
+ Requires-Dist: pytest>=8.4.1
21
+ Requires-Dist: python-dateutil>=2.8.2
22
+ Requires-Dist: python-dotenv>=1.1.0
23
+ Requires-Dist: requests>=2.32.3
24
+ Description-Content-Type: text/markdown
25
+
1
26
  # Meta Ads MCP
2
27
 
3
28
  A [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) server for interacting with Meta Ads API. This tool enables AI models to access, analyze, and manage Meta advertising campaigns through a standardized interface, allowing LLMs to retrieve performance data, visualize ad creatives, and provide strategic insights for Facebook, Instagram, and other Meta platforms.
@@ -18,6 +43,7 @@ A [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) server for in
18
43
  - [Features](#features)
19
44
  - [Configuration](#configuration)
20
45
  - [Available MCP Tools](#available-mcp-tools)
46
+ - [Licensing](#licensing)
21
47
  - [Privacy and Security](#privacy-and-security)
22
48
  - [Testing](#testing)
23
49
  - [Troubleshooting](#troubleshooting)
@@ -382,6 +408,27 @@ For local installation configuration, authentication options, and advanced techn
382
408
  - `query`: Search query string (e.g., "Injury Payouts pages", "active campaigns")
383
409
  - Returns: List of matching record IDs in ChatGPT-compatible format
384
410
 
411
+ ## Licensing
412
+
413
+ Meta Ads MCP is licensed under the [Business Source License 1.1](LICENSE). This means:
414
+
415
+ ### ✅ **What you CAN do:**
416
+ - ✅ **Use for free** - Individual and business use at no cost
417
+ - ✅ **Modify and customize** - Edit the source code for your needs
418
+ - ✅ **Internal commercial use** - Deploy within your organization
419
+ - ✅ **Redistribute** - Share the software with others
420
+ - ✅ **Create derivative works** - Build upon the codebase
421
+
422
+ ### ❌ **What you CANNOT do:**
423
+ - ❌ **Offer as competing SaaS** - Cannot offer this as a hosted service that competes with ARTELL SOLUÇÕES TECNOLÓGICAS LTDA's commercial offerings
424
+
425
+ ### 🔄 **Future Open Source:**
426
+ - **Change Date**: January 1, 2029
427
+ - **After Change Date**: Automatically converts to Apache License 2.0 (fully open source)
428
+ - **No restrictions**: After the change date, you can use it for any purpose, including competing services
429
+
430
+ This licensing model ensures the software remains accessible while protecting the commercial interests of the original developers. For questions about commercial licensing or use cases, please contact us.
431
+
385
432
  ## Privacy and Security
386
433
 
387
434
  Meta Ads MCP follows security best practices with secure token management and automatic authentication handling.
@@ -1,28 +1,3 @@
1
- Metadata-Version: 2.4
2
- Name: meta-ads-mcp
3
- Version: 0.10.0
4
- Summary: Model Context Protocol (MCP) plugin for interacting with Meta Ads API
5
- Project-URL: Homepage, https://github.com/pipeboard-co/meta-ads-mcp
6
- Project-URL: Bug Tracker, https://github.com/pipeboard-co/meta-ads-mcp/issues
7
- Author-email: Yves Junqueira <yves.junqueira@gmail.com>
8
- License: BUSL-1.1
9
- License-File: LICENSE
10
- Keywords: ads,api,claude,facebook,mcp,meta
11
- Classifier: License :: Other/Proprietary License
12
- Classifier: Operating System :: OS Independent
13
- Classifier: Programming Language :: Python :: 3
14
- Requires-Python: >=3.10
15
- Requires-Dist: httpx>=0.26.0
16
- Requires-Dist: mcp[cli]<=1.12.2,>=1.10.1
17
- Requires-Dist: pathlib>=1.0.1
18
- Requires-Dist: pillow>=10.0.0
19
- Requires-Dist: pytest-asyncio>=1.0.0
20
- Requires-Dist: pytest>=8.4.1
21
- Requires-Dist: python-dateutil>=2.8.2
22
- Requires-Dist: python-dotenv>=1.1.0
23
- Requires-Dist: requests>=2.32.3
24
- Description-Content-Type: text/markdown
25
-
26
1
  # Meta Ads MCP
27
2
 
28
3
  A [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) server for interacting with Meta Ads API. This tool enables AI models to access, analyze, and manage Meta advertising campaigns through a standardized interface, allowing LLMs to retrieve performance data, visualize ad creatives, and provide strategic insights for Facebook, Instagram, and other Meta platforms.
@@ -43,6 +18,7 @@ A [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) server for in
43
18
  - [Features](#features)
44
19
  - [Configuration](#configuration)
45
20
  - [Available MCP Tools](#available-mcp-tools)
21
+ - [Licensing](#licensing)
46
22
  - [Privacy and Security](#privacy-and-security)
47
23
  - [Testing](#testing)
48
24
  - [Troubleshooting](#troubleshooting)
@@ -407,6 +383,27 @@ For local installation configuration, authentication options, and advanced techn
407
383
  - `query`: Search query string (e.g., "Injury Payouts pages", "active campaigns")
408
384
  - Returns: List of matching record IDs in ChatGPT-compatible format
409
385
 
386
+ ## Licensing
387
+
388
+ Meta Ads MCP is licensed under the [Business Source License 1.1](LICENSE). This means:
389
+
390
+ ### ✅ **What you CAN do:**
391
+ - ✅ **Use for free** - Individual and business use at no cost
392
+ - ✅ **Modify and customize** - Edit the source code for your needs
393
+ - ✅ **Internal commercial use** - Deploy within your organization
394
+ - ✅ **Redistribute** - Share the software with others
395
+ - ✅ **Create derivative works** - Build upon the codebase
396
+
397
+ ### ❌ **What you CANNOT do:**
398
+ - ❌ **Offer as competing SaaS** - Cannot offer this as a hosted service that competes with ARTELL SOLUÇÕES TECNOLÓGICAS LTDA's commercial offerings
399
+
400
+ ### 🔄 **Future Open Source:**
401
+ - **Change Date**: January 1, 2029
402
+ - **After Change Date**: Automatically converts to Apache License 2.0 (fully open source)
403
+ - **No restrictions**: After the change date, you can use it for any purpose, including competing services
404
+
405
+ This licensing model ensures the software remains accessible while protecting the commercial interests of the original developers. For questions about commercial licensing or use cases, please contact us.
406
+
410
407
  ## Privacy and Security
411
408
 
412
409
  Meta Ads MCP follows security best practices with secure token management and automatic authentication handling.
@@ -7,7 +7,7 @@ with the Claude LLM.
7
7
 
8
8
  from meta_ads_mcp.core.server import main
9
9
 
10
- __version__ = "0.10.0"
10
+ __version__ = "0.10.1"
11
11
 
12
12
  __all__ = [
13
13
  'get_ad_accounts',
@@ -147,27 +147,16 @@ async def estimate_audience_size(
147
147
  }
148
148
  }, indent=2)
149
149
 
150
- # Build delivery estimate request
151
- endpoint = f"{account_id}/delivery_estimate"
150
+ # Build reach estimate request (using correct Meta API endpoint)
151
+ endpoint = f"{account_id}/reachestimate"
152
152
  params = {
153
- "targeting": targeting,
154
- "optimization_goal": optimization_goal
153
+ "targeting_spec": targeting
155
154
  }
156
155
 
157
- # Add basic campaign parameters for estimation
158
- if optimization_goal == "REACH":
159
- params["objective"] = "REACH"
160
- elif optimization_goal in ["LINK_CLICKS", "LANDING_PAGE_VIEWS"]:
161
- params["objective"] = "TRAFFIC"
162
- elif optimization_goal == "CONVERSIONS":
163
- params["objective"] = "CONVERSIONS"
164
- elif optimization_goal == "APP_INSTALLS":
165
- params["objective"] = "APP_INSTALLS"
166
- else:
167
- params["objective"] = "REACH" # Default fallback
156
+ # Note: reachestimate endpoint doesn't support optimization_goal or objective parameters
168
157
 
169
158
  try:
170
- data = await make_api_request(endpoint, access_token, params, method="POST")
159
+ data = await make_api_request(endpoint, access_token, params, method="GET")
171
160
 
172
161
  # Format the response for easier consumption
173
162
  if "data" in data and len(data["data"]) > 0:
@@ -191,14 +180,69 @@ async def estimate_audience_size(
191
180
  else:
192
181
  return json.dumps({
193
182
  "error": "No estimation data returned from Meta API",
194
- "raw_response": data
183
+ "raw_response": data,
184
+ "debug_info": {
185
+ "response_keys": list(data.keys()) if isinstance(data, dict) else "not_a_dict",
186
+ "response_type": str(type(data)),
187
+ "endpoint_used": f"{account_id}/reachestimate"
188
+ }
195
189
  }, indent=2)
196
190
 
197
191
  except Exception as e:
198
- return json.dumps({
199
- "error": f"Failed to get audience estimation: {str(e)}",
200
- "details": "Check targeting parameters and account permissions"
201
- }, indent=2)
192
+ # Check if this is the specific Business Manager system user permission error
193
+ error_str = str(e)
194
+ if "100" in error_str and "33" in error_str:
195
+ # Try to provide fallback estimation using individual interests if available
196
+ interests_found = []
197
+ if targeting and "interests" in targeting:
198
+ interests_found.extend([interest.get("id") for interest in targeting["interests"] if interest.get("id")])
199
+ elif targeting and "flexible_spec" in targeting:
200
+ for spec in targeting["flexible_spec"]:
201
+ if "interests" in spec:
202
+ interests_found.extend([interest.get("id") for interest in spec["interests"] if interest.get("id")])
203
+
204
+ if interests_found:
205
+ # Attempt to get individual interest data as fallback
206
+ try:
207
+ fallback_result = await estimate_audience_size(
208
+ access_token=access_token,
209
+ interest_fbid_list=interests_found
210
+ )
211
+ fallback_data = json.loads(fallback_result)
212
+
213
+ return json.dumps({
214
+ "comprehensive_targeting_failed": True,
215
+ "error_code": "100-33",
216
+ "fallback_used": True,
217
+ "details": {
218
+ "issue": "reachestimate endpoint returned error - possibly due to targeting parameters or account limitations",
219
+ "solution": "Individual interest validation used as fallback - comprehensive targeting may have specific requirements",
220
+ "endpoint_used": f"{account_id}/reachestimate"
221
+ },
222
+ "individual_interest_data": fallback_data,
223
+ "note": "Individual interest audience sizes provided as fallback. Comprehensive targeting via reachestimate endpoint failed."
224
+ }, indent=2)
225
+ except:
226
+ pass
227
+
228
+ return json.dumps({
229
+ "error": "reachestimate endpoint returned error (previously was incorrectly using delivery_estimate)",
230
+ "error_code": "100-33",
231
+ "details": {
232
+ "issue": "The endpoint returned an error, possibly due to targeting parameters or account limitations",
233
+ "endpoint_used": f"{account_id}/reachestimate",
234
+ "previous_issue": "Code was previously using non-existent delivery_estimate endpoint - now fixed",
235
+ "available_alternative": "Use interest_list or interest_fbid_list parameters for individual interest validation"
236
+ },
237
+ "raw_error": error_str
238
+ }, indent=2)
239
+ else:
240
+ return json.dumps({
241
+ "error": f"Failed to get audience estimation from reachestimate endpoint: {str(e)}",
242
+ "details": "Check targeting parameters and account permissions",
243
+ "error_type": "general_api_error",
244
+ "endpoint_used": f"{account_id}/reachestimate"
245
+ }, indent=2)
202
246
 
203
247
 
204
248
  @mcp_server.tool()
@@ -4,8 +4,8 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "meta-ads-mcp"
7
- version = "0.10.0"
8
- description = "Model Context Protocol (MCP) plugin for interacting with Meta Ads API"
7
+ version = "0.10.1"
8
+ description = "Model Context Protocol (MCP) server for interacting with Meta Ads API"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
11
11
  authors = [
@@ -3,7 +3,7 @@
3
3
  Unit tests for estimate_audience_size functionality in Meta Ads MCP.
4
4
 
5
5
  This module tests the new estimate_audience_size function that replaces validate_interests
6
- and provides comprehensive audience estimation using Meta's delivery_estimate API.
6
+ and provides comprehensive audience estimation using Meta's reachestimate API.
7
7
  """
8
8
 
9
9
  import pytest
@@ -58,14 +58,12 @@ class TestEstimateAudienceSize:
58
58
 
59
59
  # Verify API call
60
60
  mock_api.assert_called_once_with(
61
- "act_123456789/delivery_estimate",
61
+ "act_123456789/reachestimate",
62
62
  "test_token",
63
63
  {
64
- "targeting": targeting_spec,
65
- "optimization_goal": "REACH",
66
- "objective": "REACH"
64
+ "targeting_spec": targeting_spec
67
65
  },
68
- method="POST"
66
+ method="GET"
69
67
  )
70
68
 
71
69
  # Verify response format
@@ -80,7 +78,7 @@ class TestEstimateAudienceSize:
80
78
 
81
79
  @pytest.mark.asyncio
82
80
  async def test_different_optimization_goals(self):
83
- """Test audience estimation with different optimization goals"""
81
+ """Test audience estimation with different optimization goals (parameter is preserved in response)"""
84
82
  mock_response = {
85
83
  "data": [
86
84
  {
@@ -98,19 +96,13 @@ class TestEstimateAudienceSize:
98
96
  "geo_locations": {"countries": ["US"]}
99
97
  }
100
98
 
101
- test_cases = [
102
- ("REACH", "REACH"),
103
- ("LINK_CLICKS", "TRAFFIC"),
104
- ("LANDING_PAGE_VIEWS", "TRAFFIC"),
105
- ("CONVERSIONS", "CONVERSIONS"),
106
- ("APP_INSTALLS", "APP_INSTALLS"),
107
- ("UNKNOWN_GOAL", "REACH") # Should fallback to REACH
108
- ]
99
+ # Test different optimization goals - they should all use the same reachestimate endpoint
100
+ test_goals = ["REACH", "LINK_CLICKS", "LANDING_PAGE_VIEWS", "CONVERSIONS", "APP_INSTALLS"]
109
101
 
110
102
  with patch('meta_ads_mcp.core.targeting.make_api_request', new_callable=AsyncMock) as mock_api:
111
103
  mock_api.return_value = mock_response
112
104
 
113
- for optimization_goal, expected_objective in test_cases:
105
+ for optimization_goal in test_goals:
114
106
  mock_api.reset_mock()
115
107
 
116
108
  result = await estimate_audience_size(
@@ -120,16 +112,14 @@ class TestEstimateAudienceSize:
120
112
  optimization_goal=optimization_goal
121
113
  )
122
114
 
123
- # Verify correct objective mapping
115
+ # Verify API call uses reachestimate endpoint with simplified parameters
124
116
  mock_api.assert_called_once_with(
125
- "act_123456789/delivery_estimate",
117
+ "act_123456789/reachestimate",
126
118
  "test_token",
127
119
  {
128
- "targeting": targeting_spec,
129
- "optimization_goal": optimization_goal,
130
- "objective": expected_objective
120
+ "targeting_spec": targeting_spec
131
121
  },
132
- method="POST"
122
+ method="GET"
133
123
  )
134
124
 
135
125
  result_data = json.loads(result)
@@ -316,7 +306,7 @@ class TestEstimateAudienceSize:
316
306
 
317
307
  @pytest.mark.asyncio
318
308
  async def test_api_error_handling(self):
319
- """Test handling of API errors from delivery_estimate"""
309
+ """Test handling of API errors from reachestimate"""
320
310
  targeting_spec = {
321
311
  "age_min": 25,
322
312
  "age_max": 65,
@@ -338,12 +328,12 @@ class TestEstimateAudienceSize:
338
328
  assert "data" in result_data
339
329
  nested_data = json.loads(result_data["data"])
340
330
  assert "error" in nested_data
341
- assert "Failed to get audience estimation" in nested_data["error"]
331
+ assert "Failed to get audience estimation from reachestimate endpoint" in nested_data["error"]
342
332
  assert "details" in nested_data
343
333
 
344
334
  @pytest.mark.asyncio
345
335
  async def test_empty_api_response(self):
346
- """Test handling of empty response from delivery_estimate API"""
336
+ """Test handling of empty response from reachestimate API"""
347
337
  targeting_spec = {
348
338
  "age_min": 25,
349
339
  "age_max": 65,
@@ -614,7 +614,7 @@ class AudienceEstimationTester:
614
614
  print(f"\n✅ Audience estimation tests: SUCCESS ({passed_tests}/{total_tests} passed)")
615
615
  print(" • Comprehensive audience estimation is working")
616
616
  print(" • Backwards compatibility is maintained")
617
- print(" • Meta delivery_estimate API integration is functional")
617
+ print(" • Meta reachestimate API integration is functional")
618
618
  return True
619
619
  else:
620
620
  print(f"\n❌ Audience estimation tests: FAILED ({passed_tests}/{total_tests} passed)")
File without changes
File without changes
File without changes
File without changes
File without changes