meta-ads-mcp 0.4.3__py3-none-any.whl → 0.4.5__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 CHANGED
@@ -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.4.3"
10
+ __version__ = "0.4.5"
11
11
 
12
12
  __all__ = [
13
13
  'get_ad_accounts',
@@ -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: Targeting specifications including targeting_automation
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
- changes = {}
219
+ params = {}
223
220
 
224
221
  if frequency_control_specs is not None:
225
- changes['frequency_control_specs'] = frequency_control_specs
222
+ params['frequency_control_specs'] = frequency_control_specs
226
223
 
227
224
  if bid_strategy is not None:
228
- changes['bid_strategy'] = bid_strategy
225
+ params['bid_strategy'] = bid_strategy
229
226
 
230
227
  if bid_amount is not None:
231
- changes['bid_amount'] = bid_amount
228
+ params['bid_amount'] = str(bid_amount)
232
229
 
233
230
  if status is not None:
234
- changes['status'] = status
231
+ params['status'] = status
235
232
 
236
233
  if optimization_goal is not None:
237
- changes['optimization_goal'] = optimization_goal
234
+ params['optimization_goal'] = optimization_goal
238
235
 
239
236
  if targeting is not None:
240
- # Get current ad set details to preserve existing targeting settings
241
- current_details_json = await get_adset_details(adset_id=adset_id, access_token=access_token)
242
- current_details = json.loads(current_details_json)
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
- # Full targeting replacement
263
- changes['targeting'] = targeting
241
+ params['targeting'] = targeting # Already a string
264
242
 
265
- if not changes:
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
- port = start_callback_server()
275
-
276
- # Generate confirmation URL with properly encoded parameters
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": "Callback server disabled",
283
- "message": f"Cannot create confirmation URL: {str(e)}",
284
- "suggestion": "Manual update confirmation not available when META_ADS_DISABLE_CALLBACK_SERVER is set",
285
- "adset_id": adset_id,
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)
meta_ads_mcp/core/auth.py CHANGED
@@ -23,7 +23,9 @@ from .callback_server import (
23
23
  from .pipeboard_auth import pipeboard_auth_manager
24
24
 
25
25
  # Auth constants
26
- AUTH_SCOPE = "ads_management,ads_read,business_management,public_profile"
26
+ # Scope includes pages_show_list and pages_read_engagement to fix issue #16
27
+ # where get_account_pages failed for regular users due to missing page permissions
28
+ AUTH_SCOPE = "ads_management,ads_read,business_management,public_profile,pages_show_list,pages_read_engagement"
27
29
  AUTH_REDIRECT_URI = "http://localhost:8888/callback"
28
30
  AUTH_RESPONSE_TYPE = "token"
29
31