meta-ads-mcp 0.11.4__py3-none-any.whl → 0.11.6__py3-none-any.whl
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.
- meta_ads_mcp/__init__.py +1 -1
- meta_ads_mcp/core/api.py +8 -1
- meta_ads_mcp/core/duplication.py +4 -4
- meta_ads_mcp/core/targeting.py +224 -18
- {meta_ads_mcp-0.11.4.dist-info → meta_ads_mcp-0.11.6.dist-info}/METADATA +1 -1
- {meta_ads_mcp-0.11.4.dist-info → meta_ads_mcp-0.11.6.dist-info}/RECORD +9 -9
- {meta_ads_mcp-0.11.4.dist-info → meta_ads_mcp-0.11.6.dist-info}/WHEEL +0 -0
- {meta_ads_mcp-0.11.4.dist-info → meta_ads_mcp-0.11.6.dist-info}/entry_points.txt +0 -0
- {meta_ads_mcp-0.11.4.dist-info → meta_ads_mcp-0.11.6.dist-info}/licenses/LICENSE +0 -0
meta_ads_mcp/__init__.py
CHANGED
meta_ads_mcp/core/api.py
CHANGED
|
@@ -87,7 +87,14 @@ async def make_api_request(
|
|
|
87
87
|
async with httpx.AsyncClient() as client:
|
|
88
88
|
try:
|
|
89
89
|
if method == "GET":
|
|
90
|
-
|
|
90
|
+
# For GET, JSON-encode dict/list params (e.g., targeting_spec) to proper strings
|
|
91
|
+
encoded_params = {}
|
|
92
|
+
for key, value in request_params.items():
|
|
93
|
+
if isinstance(value, (dict, list)):
|
|
94
|
+
encoded_params[key] = json.dumps(value)
|
|
95
|
+
else:
|
|
96
|
+
encoded_params[key] = value
|
|
97
|
+
response = await client.get(url, params=encoded_params, headers=headers, timeout=30.0)
|
|
91
98
|
elif method == "POST":
|
|
92
99
|
# For Meta API, POST requests need data, not JSON
|
|
93
100
|
if 'targeting' in request_params and isinstance(request_params['targeting'], dict):
|
meta_ads_mcp/core/duplication.py
CHANGED
|
@@ -30,7 +30,7 @@ if ENABLE_DUPLICATION:
|
|
|
30
30
|
"""
|
|
31
31
|
Duplicate a Meta Ads campaign with all its ad sets and ads.
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
Recommended: Use this to run robust experiments.
|
|
34
34
|
|
|
35
35
|
Args:
|
|
36
36
|
campaign_id: Meta Ads campaign ID to duplicate
|
|
@@ -73,7 +73,7 @@ if ENABLE_DUPLICATION:
|
|
|
73
73
|
"""
|
|
74
74
|
Duplicate a Meta Ads ad set with its ads.
|
|
75
75
|
|
|
76
|
-
|
|
76
|
+
Recommended: Use this to run robust experiments.
|
|
77
77
|
|
|
78
78
|
Args:
|
|
79
79
|
adset_id: Meta Ads ad set ID to duplicate
|
|
@@ -114,7 +114,7 @@ if ENABLE_DUPLICATION:
|
|
|
114
114
|
"""
|
|
115
115
|
Duplicate a Meta Ads ad.
|
|
116
116
|
|
|
117
|
-
|
|
117
|
+
Recommended: Use this to run robust experiments.
|
|
118
118
|
|
|
119
119
|
Args:
|
|
120
120
|
ad_id: Meta Ads ad ID to duplicate
|
|
@@ -152,7 +152,7 @@ if ENABLE_DUPLICATION:
|
|
|
152
152
|
"""
|
|
153
153
|
Duplicate a Meta Ads creative.
|
|
154
154
|
|
|
155
|
-
|
|
155
|
+
Recommended: Use this to run robust experiments.
|
|
156
156
|
|
|
157
157
|
Args:
|
|
158
158
|
creative_id: Meta Ads creative ID to duplicate
|
meta_ads_mcp/core/targeting.py
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import json
|
|
4
4
|
from typing import Optional, List, Dict, Any
|
|
5
|
+
import os
|
|
5
6
|
from .api import meta_api_tool, make_api_request
|
|
6
7
|
from .server import mcp_server
|
|
7
8
|
|
|
@@ -147,6 +148,49 @@ async def estimate_audience_size(
|
|
|
147
148
|
}
|
|
148
149
|
}, indent=2)
|
|
149
150
|
|
|
151
|
+
# Preflight validation: require at least one location OR a custom audience
|
|
152
|
+
def _has_location_or_custom_audience(t: Dict[str, Any]) -> bool:
|
|
153
|
+
if not isinstance(t, dict):
|
|
154
|
+
return False
|
|
155
|
+
geo = t.get("geo_locations") or {}
|
|
156
|
+
if isinstance(geo, dict):
|
|
157
|
+
for key in [
|
|
158
|
+
"countries",
|
|
159
|
+
"regions",
|
|
160
|
+
"cities",
|
|
161
|
+
"zips",
|
|
162
|
+
"geo_markets",
|
|
163
|
+
"country_groups"
|
|
164
|
+
]:
|
|
165
|
+
val = geo.get(key)
|
|
166
|
+
if isinstance(val, list) and len(val) > 0:
|
|
167
|
+
return True
|
|
168
|
+
# Top-level custom audiences
|
|
169
|
+
ca = t.get("custom_audiences")
|
|
170
|
+
if isinstance(ca, list) and len(ca) > 0:
|
|
171
|
+
return True
|
|
172
|
+
# Custom audiences within flexible_spec
|
|
173
|
+
flex = t.get("flexible_spec")
|
|
174
|
+
if isinstance(flex, list):
|
|
175
|
+
for spec in flex:
|
|
176
|
+
if isinstance(spec, dict):
|
|
177
|
+
ca_spec = spec.get("custom_audiences")
|
|
178
|
+
if isinstance(ca_spec, list) and len(ca_spec) > 0:
|
|
179
|
+
return True
|
|
180
|
+
return False
|
|
181
|
+
|
|
182
|
+
if not _has_location_or_custom_audience(targeting):
|
|
183
|
+
return json.dumps({
|
|
184
|
+
"error": "Missing target audience location",
|
|
185
|
+
"details": "Select at least one location in targeting.geo_locations or include a custom audience.",
|
|
186
|
+
"action_required": "Add geo_locations with countries/regions/cities/zips or include custom_audiences.",
|
|
187
|
+
"example": {
|
|
188
|
+
"geo_locations": {"countries": ["US"]},
|
|
189
|
+
"age_min": 25,
|
|
190
|
+
"age_max": 65
|
|
191
|
+
}
|
|
192
|
+
}, indent=2)
|
|
193
|
+
|
|
150
194
|
# Build reach estimate request (using correct Meta API endpoint)
|
|
151
195
|
endpoint = f"{account_id}/reachestimate"
|
|
152
196
|
params = {
|
|
@@ -158,25 +202,153 @@ async def estimate_audience_size(
|
|
|
158
202
|
try:
|
|
159
203
|
data = await make_api_request(endpoint, access_token, params, method="GET")
|
|
160
204
|
|
|
205
|
+
# Surface Graph API errors directly for better diagnostics.
|
|
206
|
+
# If reachestimate fails, optionally attempt a fallback using delivery_estimate.
|
|
207
|
+
if isinstance(data, dict) and "error" in data:
|
|
208
|
+
# Special handling for Missing Target Audience Location error (subcode 1885364)
|
|
209
|
+
try:
|
|
210
|
+
err_wrapper = data.get("error", {})
|
|
211
|
+
details_obj = err_wrapper.get("details", {})
|
|
212
|
+
raw_err = details_obj.get("error", {}) if isinstance(details_obj, dict) else {}
|
|
213
|
+
if (
|
|
214
|
+
isinstance(raw_err, dict) and (
|
|
215
|
+
raw_err.get("error_subcode") == 1885364 or
|
|
216
|
+
raw_err.get("error_user_title") == "Missing Target Audience Location"
|
|
217
|
+
)
|
|
218
|
+
):
|
|
219
|
+
return json.dumps({
|
|
220
|
+
"error": "Missing target audience location",
|
|
221
|
+
"details": raw_err.get("error_user_msg") or "Select at least one location, or choose a custom audience.",
|
|
222
|
+
"endpoint_used": f"{account_id}/reachestimate",
|
|
223
|
+
"action_required": "Add geo_locations with at least one of countries/regions/cities/zips or include custom_audiences.",
|
|
224
|
+
"blame_field_specs": raw_err.get("error_data", {}).get("blame_field_specs") if isinstance(raw_err.get("error_data"), dict) else None
|
|
225
|
+
}, indent=2)
|
|
226
|
+
except Exception:
|
|
227
|
+
pass
|
|
228
|
+
# Allow disabling fallback via environment variable
|
|
229
|
+
# Default: fallback disabled unless explicitly enabled by setting DISABLE flag to "0"
|
|
230
|
+
disable_fallback = os.environ.get("META_MCP_DISABLE_DELIVERY_FALLBACK", "1") == "1"
|
|
231
|
+
if disable_fallback:
|
|
232
|
+
return json.dumps({
|
|
233
|
+
"error": "Graph API returned an error for reachestimate",
|
|
234
|
+
"details": data.get("error"),
|
|
235
|
+
"endpoint_used": f"{account_id}/reachestimate",
|
|
236
|
+
"request_params": {
|
|
237
|
+
"has_targeting_spec": bool(targeting),
|
|
238
|
+
},
|
|
239
|
+
"note": "delivery_estimate fallback disabled via META_MCP_DISABLE_DELIVERY_FALLBACK"
|
|
240
|
+
}, indent=2)
|
|
241
|
+
|
|
242
|
+
# Try fallback to delivery_estimate endpoint
|
|
243
|
+
try:
|
|
244
|
+
fallback_endpoint = f"{account_id}/delivery_estimate"
|
|
245
|
+
fallback_params = {
|
|
246
|
+
"targeting_spec": json.dumps(targeting),
|
|
247
|
+
# Some API versions accept optimization_goal here
|
|
248
|
+
"optimization_goal": optimization_goal
|
|
249
|
+
}
|
|
250
|
+
fallback_data = await make_api_request(fallback_endpoint, access_token, fallback_params, method="GET")
|
|
251
|
+
|
|
252
|
+
# If fallback returns usable data, format similarly
|
|
253
|
+
if isinstance(fallback_data, dict) and "data" in fallback_data and len(fallback_data["data"]) > 0:
|
|
254
|
+
estimate_data = fallback_data["data"][0]
|
|
255
|
+
formatted_response = {
|
|
256
|
+
"success": True,
|
|
257
|
+
"account_id": account_id,
|
|
258
|
+
"targeting": targeting,
|
|
259
|
+
"optimization_goal": optimization_goal,
|
|
260
|
+
"estimated_audience_size": estimate_data.get("estimate_mau", 0),
|
|
261
|
+
"estimate_details": {
|
|
262
|
+
"monthly_active_users": estimate_data.get("estimate_mau", 0),
|
|
263
|
+
"daily_outcomes_curve": estimate_data.get("estimate_dau", []),
|
|
264
|
+
"bid_estimate": estimate_data.get("bid_estimates", {}),
|
|
265
|
+
"unsupported_targeting": estimate_data.get("unsupported_targeting", [])
|
|
266
|
+
},
|
|
267
|
+
"raw_response": fallback_data,
|
|
268
|
+
"fallback_endpoint_used": "delivery_estimate"
|
|
269
|
+
}
|
|
270
|
+
return json.dumps(formatted_response, indent=2)
|
|
271
|
+
|
|
272
|
+
# Fallback returned but not in expected format
|
|
273
|
+
return json.dumps({
|
|
274
|
+
"error": "Graph API returned an error for reachestimate; delivery_estimate fallback did not return usable data",
|
|
275
|
+
"reachestimate_error": data.get("error"),
|
|
276
|
+
"fallback_endpoint_used": "delivery_estimate",
|
|
277
|
+
"fallback_raw_response": fallback_data,
|
|
278
|
+
"endpoint_used": f"{account_id}/reachestimate",
|
|
279
|
+
"request_params": {
|
|
280
|
+
"has_targeting_spec": bool(targeting)
|
|
281
|
+
}
|
|
282
|
+
}, indent=2)
|
|
283
|
+
except Exception as _fallback_exc:
|
|
284
|
+
return json.dumps({
|
|
285
|
+
"error": "Graph API returned an error for reachestimate; delivery_estimate fallback also failed",
|
|
286
|
+
"reachestimate_error": data.get("error"),
|
|
287
|
+
"fallback_endpoint_used": "delivery_estimate",
|
|
288
|
+
"fallback_exception": str(_fallback_exc),
|
|
289
|
+
"endpoint_used": f"{account_id}/reachestimate",
|
|
290
|
+
"request_params": {
|
|
291
|
+
"has_targeting_spec": bool(targeting)
|
|
292
|
+
}
|
|
293
|
+
}, indent=2)
|
|
294
|
+
|
|
161
295
|
# Format the response for easier consumption
|
|
162
|
-
if "data" in data
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
"
|
|
172
|
-
"
|
|
173
|
-
"
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
296
|
+
if "data" in data:
|
|
297
|
+
response_data = data["data"]
|
|
298
|
+
# Case 1: delivery_estimate-like list structure
|
|
299
|
+
if isinstance(response_data, list) and len(response_data) > 0:
|
|
300
|
+
estimate_data = response_data[0]
|
|
301
|
+
formatted_response = {
|
|
302
|
+
"success": True,
|
|
303
|
+
"account_id": account_id,
|
|
304
|
+
"targeting": targeting,
|
|
305
|
+
"optimization_goal": optimization_goal,
|
|
306
|
+
"estimated_audience_size": estimate_data.get("estimate_mau", 0),
|
|
307
|
+
"estimate_details": {
|
|
308
|
+
"monthly_active_users": estimate_data.get("estimate_mau", 0),
|
|
309
|
+
"daily_outcomes_curve": estimate_data.get("estimate_dau", []),
|
|
310
|
+
"bid_estimate": estimate_data.get("bid_estimates", {}),
|
|
311
|
+
"unsupported_targeting": estimate_data.get("unsupported_targeting", [])
|
|
312
|
+
},
|
|
313
|
+
"raw_response": data
|
|
314
|
+
}
|
|
315
|
+
return json.dumps(formatted_response, indent=2)
|
|
316
|
+
# Case 1b: explicit handling for empty list responses
|
|
317
|
+
if isinstance(response_data, list) and len(response_data) == 0:
|
|
318
|
+
return json.dumps({
|
|
319
|
+
"error": "No estimation data returned from Meta API",
|
|
320
|
+
"raw_response": data,
|
|
321
|
+
"debug_info": {
|
|
322
|
+
"response_keys": list(data.keys()) if isinstance(data, dict) else "not_a_dict",
|
|
323
|
+
"response_type": str(type(data)),
|
|
324
|
+
"endpoint_used": f"{account_id}/reachestimate"
|
|
325
|
+
}
|
|
326
|
+
}, indent=2)
|
|
327
|
+
# Case 2: reachestimate dict structure with bounds
|
|
328
|
+
if isinstance(response_data, dict):
|
|
329
|
+
lower = response_data.get("users_lower_bound", response_data.get("estimate_mau_lower_bound"))
|
|
330
|
+
upper = response_data.get("users_upper_bound", response_data.get("estimate_mau_upper_bound"))
|
|
331
|
+
estimate_ready = response_data.get("estimate_ready")
|
|
332
|
+
midpoint = None
|
|
333
|
+
try:
|
|
334
|
+
if isinstance(lower, (int, float)) and isinstance(upper, (int, float)):
|
|
335
|
+
midpoint = int((lower + upper) / 2)
|
|
336
|
+
except Exception:
|
|
337
|
+
midpoint = None
|
|
338
|
+
formatted_response = {
|
|
339
|
+
"success": True,
|
|
340
|
+
"account_id": account_id,
|
|
341
|
+
"targeting": targeting,
|
|
342
|
+
"optimization_goal": optimization_goal,
|
|
343
|
+
"estimated_audience_size": midpoint if midpoint is not None else 0,
|
|
344
|
+
"estimate_details": {
|
|
345
|
+
"users_lower_bound": lower,
|
|
346
|
+
"users_upper_bound": upper,
|
|
347
|
+
"estimate_ready": estimate_ready
|
|
348
|
+
},
|
|
349
|
+
"raw_response": data
|
|
350
|
+
}
|
|
351
|
+
return json.dumps(formatted_response, indent=2)
|
|
180
352
|
else:
|
|
181
353
|
return json.dumps({
|
|
182
354
|
"error": "No estimation data returned from Meta API",
|
|
@@ -189,6 +361,40 @@ async def estimate_audience_size(
|
|
|
189
361
|
}, indent=2)
|
|
190
362
|
|
|
191
363
|
except Exception as e:
|
|
364
|
+
# Try fallback to delivery_estimate first when an exception occurs (unless disabled)
|
|
365
|
+
# Default: fallback disabled unless explicitly enabled by setting DISABLE flag to "0"
|
|
366
|
+
disable_fallback = os.environ.get("META_MCP_DISABLE_DELIVERY_FALLBACK", "1") == "1"
|
|
367
|
+
if not disable_fallback:
|
|
368
|
+
try:
|
|
369
|
+
fallback_endpoint = f"{account_id}/delivery_estimate"
|
|
370
|
+
fallback_params = {
|
|
371
|
+
"targeting_spec": json.dumps(targeting) if isinstance(targeting, dict) else targeting,
|
|
372
|
+
"optimization_goal": optimization_goal
|
|
373
|
+
}
|
|
374
|
+
fallback_data = await make_api_request(fallback_endpoint, access_token, fallback_params, method="GET")
|
|
375
|
+
|
|
376
|
+
if isinstance(fallback_data, dict) and "data" in fallback_data and len(fallback_data["data"]) > 0:
|
|
377
|
+
estimate_data = fallback_data["data"][0]
|
|
378
|
+
formatted_response = {
|
|
379
|
+
"success": True,
|
|
380
|
+
"account_id": account_id,
|
|
381
|
+
"targeting": targeting,
|
|
382
|
+
"optimization_goal": optimization_goal,
|
|
383
|
+
"estimated_audience_size": estimate_data.get("estimate_mau", 0),
|
|
384
|
+
"estimate_details": {
|
|
385
|
+
"monthly_active_users": estimate_data.get("estimate_mau", 0),
|
|
386
|
+
"daily_outcomes_curve": estimate_data.get("estimate_dau", []),
|
|
387
|
+
"bid_estimate": estimate_data.get("bid_estimates", {}),
|
|
388
|
+
"unsupported_targeting": estimate_data.get("unsupported_targeting", [])
|
|
389
|
+
},
|
|
390
|
+
"raw_response": fallback_data,
|
|
391
|
+
"fallback_endpoint_used": "delivery_estimate"
|
|
392
|
+
}
|
|
393
|
+
return json.dumps(formatted_response, indent=2)
|
|
394
|
+
except Exception as _fallback_exc:
|
|
395
|
+
# If fallback also fails, proceed to detailed error handling below
|
|
396
|
+
pass
|
|
397
|
+
|
|
192
398
|
# Check if this is the specific Business Manager system user permission error
|
|
193
399
|
error_str = str(e)
|
|
194
400
|
if "100" in error_str and "33" in error_str:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: meta-ads-mcp
|
|
3
|
-
Version: 0.11.
|
|
3
|
+
Version: 0.11.6
|
|
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
|
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
meta_ads_mcp/__init__.py,sha256=
|
|
1
|
+
meta_ads_mcp/__init__.py,sha256=gdm32l1E4vgI6a8WI_vbxqmq9Tfvl4g4TeWUcZkSWbg,1477
|
|
2
2
|
meta_ads_mcp/__main__.py,sha256=XaQt3iXftG_7f0Zu7Wop9SeFgrD2WBn0EQOaPMc27d8,207
|
|
3
3
|
meta_ads_mcp/core/__init__.py,sha256=IEJtqpyUo0CZSUWeQPljQ-D2vKorTFwXnpBQWSi1hIM,1819
|
|
4
4
|
meta_ads_mcp/core/accounts.py,sha256=7Zoqq0zMIJi_Xsxe9-_b3EYx-UTeieJJvO7HxVRuUS0,4327
|
|
5
5
|
meta_ads_mcp/core/ads.py,sha256=5ulQ4p3lLo1_sIiAoMdyeo0O2y7Yq2zSDuvqkNiqX1c,61376
|
|
6
6
|
meta_ads_mcp/core/ads_library.py,sha256=smGz9FhM6RIUjlQT4Jv1BaZmXahGdK21eRCB7QMhK-4,3228
|
|
7
7
|
meta_ads_mcp/core/adsets.py,sha256=3Ok3EwPTReKshtsVs4gRMlws6LMTUJTb4ZeGPPM8JR8,16570
|
|
8
|
-
meta_ads_mcp/core/api.py,sha256=
|
|
8
|
+
meta_ads_mcp/core/api.py,sha256=dR1h3sTnSmIdEz8OSttqELA0YxBkbjMnSflgM5LsJ1A,16858
|
|
9
9
|
meta_ads_mcp/core/auth.py,sha256=l_IvejK2KYXg8yhBiP0ifE6mGwJ6ZujqYQbVw1KOUME,23649
|
|
10
10
|
meta_ads_mcp/core/authentication.py,sha256=ftoKec1HpfYCCVYIVKUD3ezvVAk6n_CJlBuePn8fzpM,10547
|
|
11
11
|
meta_ads_mcp/core/budget_schedules.py,sha256=FVyJuKbjUE4cmtlPJEbIwpN6JmU-1WQjc7io1y5JaHE,2911
|
|
12
12
|
meta_ads_mcp/core/callback_server.py,sha256=LIAJv9DW--83kdZ7VWWZal8xEprYjRZ8iug4rMczYbQ,9372
|
|
13
13
|
meta_ads_mcp/core/campaigns.py,sha256=m24epO1QmyBVBXfqHIFZAi6sRCTlOGLQckjy0azqkvo,14319
|
|
14
|
-
meta_ads_mcp/core/duplication.py,sha256=
|
|
14
|
+
meta_ads_mcp/core/duplication.py,sha256=ae9GisFg9MMXbgX8zqb6ekEFIv7DGNCMV-lwkDXSL-c,18928
|
|
15
15
|
meta_ads_mcp/core/http_auth_integration.py,sha256=lGpKhfzJcyWugBcYEvypY-qnlt-3UDBLqh7xAUH0DGw,12473
|
|
16
16
|
meta_ads_mcp/core/insights.py,sha256=O8eldZG7wi46_heVgnOGiHPK3M_YNzvfT81wZQt9m0Q,4710
|
|
17
17
|
meta_ads_mcp/core/openai_deep_research.py,sha256=68ayGopnBSPEYhN9R1sFvTXtyWtM0lji9aWS3uSXnLY,18649
|
|
@@ -19,10 +19,10 @@ meta_ads_mcp/core/pipeboard_auth.py,sha256=vv0yc4RBcGOm7VOovud-QNV1JmvBF-njOKICz
|
|
|
19
19
|
meta_ads_mcp/core/reports.py,sha256=2pXYCCjYc3MZ8GlrbSHND436W62WlbfbtMll1dfJdqE,5750
|
|
20
20
|
meta_ads_mcp/core/resources.py,sha256=-zIIfZulpo76vcKv6jhAlQq91cR2SZ3cjYZt3ek3x0w,1236
|
|
21
21
|
meta_ads_mcp/core/server.py,sha256=9SlgM_qvdlxo24ctnZzLgW1e1nfAspCSx3YyJQkKP64,17856
|
|
22
|
-
meta_ads_mcp/core/targeting.py,sha256
|
|
22
|
+
meta_ads_mcp/core/targeting.py,sha256=d2uLWbIEtucRuTgwZEdtVKLDZJgaxQ1lDtZ0ZgkBJC4,25150
|
|
23
23
|
meta_ads_mcp/core/utils.py,sha256=ytj41yC5SqduLrAiZYBSd6OUwlJRaIClTwnnYKpNFds,9387
|
|
24
|
-
meta_ads_mcp-0.11.
|
|
25
|
-
meta_ads_mcp-0.11.
|
|
26
|
-
meta_ads_mcp-0.11.
|
|
27
|
-
meta_ads_mcp-0.11.
|
|
28
|
-
meta_ads_mcp-0.11.
|
|
24
|
+
meta_ads_mcp-0.11.6.dist-info/METADATA,sha256=8ZciePGi_K5-6VYwh7D50rbJDjPVLlyLHpAv0JWFWAA,24245
|
|
25
|
+
meta_ads_mcp-0.11.6.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
26
|
+
meta_ads_mcp-0.11.6.dist-info/entry_points.txt,sha256=Dv2RkoBjRJBqj6CyhwqGIiwPCD-SCL1-7B9-zmVRuv0,57
|
|
27
|
+
meta_ads_mcp-0.11.6.dist-info/licenses/LICENSE,sha256=E2d762fbhwKRYn8o7J6Szr6vyBPrHVDlK3jbHPx-d84,3851
|
|
28
|
+
meta_ads_mcp-0.11.6.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|