meta-ads-mcp 0.4.2__py3-none-any.whl → 0.4.4__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/ads.py +9 -8
- meta_ads_mcp/core/adsets.py +24 -69
- meta_ads_mcp/core/callback_server.py +142 -906
- {meta_ads_mcp-0.4.2.dist-info → meta_ads_mcp-0.4.4.dist-info}/METADATA +1 -1
- {meta_ads_mcp-0.4.2.dist-info → meta_ads_mcp-0.4.4.dist-info}/RECORD +9 -9
- {meta_ads_mcp-0.4.2.dist-info → meta_ads_mcp-0.4.4.dist-info}/WHEEL +0 -0
- {meta_ads_mcp-0.4.2.dist-info → meta_ads_mcp-0.4.4.dist-info}/entry_points.txt +0 -0
- {meta_ads_mcp-0.4.2.dist-info → meta_ads_mcp-0.4.4.dist-info}/licenses/LICENSE +0 -0
meta_ads_mcp/__init__.py
CHANGED
meta_ads_mcp/core/ads.py
CHANGED
|
@@ -38,26 +38,27 @@ async def get_ads(access_token: str = None, account_id: str = None, limit: int =
|
|
|
38
38
|
else:
|
|
39
39
|
return json.dumps({"error": "No account ID specified and no accounts found for user"}, indent=2)
|
|
40
40
|
|
|
41
|
+
# Prioritize adset_id over campaign_id - use adset-specific endpoint
|
|
42
|
+
if adset_id:
|
|
43
|
+
endpoint = f"{adset_id}/ads"
|
|
44
|
+
params = {
|
|
45
|
+
"fields": "id,name,adset_id,campaign_id,status,creative,created_time,updated_time,bid_amount,conversion_domain,tracking_specs",
|
|
46
|
+
"limit": limit
|
|
47
|
+
}
|
|
41
48
|
# Use campaign-specific endpoint if campaign_id is provided
|
|
42
|
-
|
|
49
|
+
elif campaign_id:
|
|
43
50
|
endpoint = f"{campaign_id}/ads"
|
|
44
51
|
params = {
|
|
45
52
|
"fields": "id,name,adset_id,campaign_id,status,creative,created_time,updated_time,bid_amount,conversion_domain,tracking_specs",
|
|
46
53
|
"limit": limit
|
|
47
54
|
}
|
|
48
|
-
# Adset ID can still be used to filter within the campaign
|
|
49
|
-
if adset_id:
|
|
50
|
-
params["adset_id"] = adset_id
|
|
51
55
|
else:
|
|
52
|
-
# Default to account-level endpoint if no
|
|
56
|
+
# Default to account-level endpoint if no specific filters
|
|
53
57
|
endpoint = f"{account_id}/ads"
|
|
54
58
|
params = {
|
|
55
59
|
"fields": "id,name,adset_id,campaign_id,status,creative,created_time,updated_time,bid_amount,conversion_domain,tracking_specs",
|
|
56
60
|
"limit": limit
|
|
57
61
|
}
|
|
58
|
-
# Adset ID can filter at the account level if no campaign specified
|
|
59
|
-
if adset_id:
|
|
60
|
-
params["adset_id"] = adset_id
|
|
61
62
|
|
|
62
63
|
data = await make_api_request(endpoint, access_token, params)
|
|
63
64
|
|
meta_ads_mcp/core/adsets.py
CHANGED
|
@@ -5,9 +5,6 @@ from typing import Optional, Dict, Any, List
|
|
|
5
5
|
from .api import meta_api_tool, make_api_request
|
|
6
6
|
from .accounts import get_ad_accounts
|
|
7
7
|
from .server import mcp_server
|
|
8
|
-
import asyncio
|
|
9
|
-
from .callback_server import start_callback_server, shutdown_callback_server, update_confirmation
|
|
10
|
-
import urllib.parse
|
|
11
8
|
|
|
12
9
|
|
|
13
10
|
@mcp_server.tool()
|
|
@@ -211,94 +208,52 @@ async def update_adset(adset_id: str, frequency_control_specs: List[Dict[str, An
|
|
|
211
208
|
bid_strategy: Bid strategy (e.g., 'LOWEST_COST_WITH_BID_CAP')
|
|
212
209
|
bid_amount: Bid amount in account currency (in cents for USD)
|
|
213
210
|
status: Update ad set status (ACTIVE, PAUSED, etc.)
|
|
214
|
-
targeting:
|
|
215
|
-
(e.g. {"targeting_automation":{"advantage_audience":1}})
|
|
211
|
+
targeting: Complete targeting specifications (will replace existing targeting)
|
|
212
|
+
(e.g. {"targeting_automation":{"advantage_audience":1}, "geo_locations": {"countries": ["US"]}})
|
|
216
213
|
optimization_goal: Conversion optimization goal (e.g., 'LINK_CLICKS', 'CONVERSIONS', 'APP_INSTALLS', etc.)
|
|
217
214
|
access_token: Meta API access token (optional - will use cached token if not provided)
|
|
218
215
|
"""
|
|
219
216
|
if not adset_id:
|
|
220
217
|
return json.dumps({"error": "No ad set ID provided"}, indent=2)
|
|
221
218
|
|
|
222
|
-
|
|
219
|
+
params = {}
|
|
223
220
|
|
|
224
221
|
if frequency_control_specs is not None:
|
|
225
|
-
|
|
222
|
+
params['frequency_control_specs'] = frequency_control_specs
|
|
226
223
|
|
|
227
224
|
if bid_strategy is not None:
|
|
228
|
-
|
|
225
|
+
params['bid_strategy'] = bid_strategy
|
|
229
226
|
|
|
230
227
|
if bid_amount is not None:
|
|
231
|
-
|
|
228
|
+
params['bid_amount'] = str(bid_amount)
|
|
232
229
|
|
|
233
230
|
if status is not None:
|
|
234
|
-
|
|
231
|
+
params['status'] = status
|
|
235
232
|
|
|
236
233
|
if optimization_goal is not None:
|
|
237
|
-
|
|
234
|
+
params['optimization_goal'] = optimization_goal
|
|
238
235
|
|
|
239
236
|
if targeting is not None:
|
|
240
|
-
#
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
# Check if the current ad set has targeting information
|
|
245
|
-
current_targeting = current_details.get('targeting', {})
|
|
246
|
-
|
|
247
|
-
if 'targeting_automation' in targeting:
|
|
248
|
-
# Only update targeting_automation while preserving other targeting settings
|
|
249
|
-
if current_targeting:
|
|
250
|
-
merged_targeting = current_targeting.copy()
|
|
251
|
-
merged_targeting['targeting_automation'] = targeting['targeting_automation']
|
|
252
|
-
changes['targeting'] = merged_targeting
|
|
253
|
-
else:
|
|
254
|
-
# If there's no existing targeting, we need to create a basic one
|
|
255
|
-
# Meta requires at least a geo_locations setting
|
|
256
|
-
basic_targeting = {
|
|
257
|
-
'targeting_automation': targeting['targeting_automation'],
|
|
258
|
-
'geo_locations': {'countries': ['US']} # Using US as default location
|
|
259
|
-
}
|
|
260
|
-
changes['targeting'] = basic_targeting
|
|
237
|
+
# Ensure proper JSON encoding for targeting
|
|
238
|
+
if isinstance(targeting, dict):
|
|
239
|
+
params['targeting'] = json.dumps(targeting)
|
|
261
240
|
else:
|
|
262
|
-
|
|
263
|
-
changes['targeting'] = targeting
|
|
241
|
+
params['targeting'] = targeting # Already a string
|
|
264
242
|
|
|
265
|
-
if not
|
|
243
|
+
if not params:
|
|
266
244
|
return json.dumps({"error": "No update parameters provided"}, indent=2)
|
|
245
|
+
|
|
246
|
+
endpoint = f"{adset_id}"
|
|
267
247
|
|
|
268
|
-
# Get current ad set details for comparison
|
|
269
|
-
current_details_json = await get_adset_details(adset_id=adset_id, access_token=access_token)
|
|
270
|
-
current_details = json.loads(current_details_json)
|
|
271
|
-
|
|
272
|
-
# Start the callback server if not already running
|
|
273
248
|
try:
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
changes_json = json.dumps(changes)
|
|
278
|
-
encoded_changes = urllib.parse.quote(changes_json)
|
|
279
|
-
confirmation_url = f"http://localhost:{port}/confirm-update?adset_id={adset_id}&token={access_token}&changes={encoded_changes}"
|
|
249
|
+
# Use POST method for updates as per Meta API documentation
|
|
250
|
+
data = await make_api_request(endpoint, access_token, params, method="POST")
|
|
251
|
+
return json.dumps(data, indent=2)
|
|
280
252
|
except Exception as e:
|
|
253
|
+
error_msg = str(e)
|
|
254
|
+
# Include adset_id in error for better context
|
|
281
255
|
return json.dumps({
|
|
282
|
-
"error": "
|
|
283
|
-
"
|
|
284
|
-
"
|
|
285
|
-
|
|
286
|
-
"proposed_changes": changes
|
|
287
|
-
}, indent=2)
|
|
288
|
-
|
|
289
|
-
# Reset the update confirmation
|
|
290
|
-
update_confirmation.clear()
|
|
291
|
-
update_confirmation.update({"approved": False})
|
|
292
|
-
|
|
293
|
-
# Return the confirmation link
|
|
294
|
-
response = {
|
|
295
|
-
"message": "Please confirm the ad set update",
|
|
296
|
-
"confirmation_url": confirmation_url,
|
|
297
|
-
"markdown_link": f"[Click here to confirm ad set update]({confirmation_url})",
|
|
298
|
-
"current_details": current_details,
|
|
299
|
-
"proposed_changes": changes,
|
|
300
|
-
"instructions_for_llm": "You must present this link as clickable Markdown to the user using the markdown_link format provided.",
|
|
301
|
-
"note": "Click the link to confirm and apply your ad set updates. Refresh the browser page if it doesn't load immediately."
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
return json.dumps(response, indent=2)
|
|
256
|
+
"error": f"Failed to update ad set {adset_id}",
|
|
257
|
+
"details": error_msg,
|
|
258
|
+
"params_sent": params
|
|
259
|
+
}, indent=2)
|