meta-ads-mcp 0.3.2__py3-none-any.whl → 0.3.3__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.3.2"
10
+ __version__ = "0.3.3"
11
11
 
12
12
  __all__ = [
13
13
  'get_ad_accounts',
@@ -9,6 +9,7 @@ from .insights import get_insights, debug_image_download
9
9
  from .authentication import get_login_link
10
10
  from .server import login_cli, main
11
11
  from .auth import login
12
+ from .ads_library import search_ads_archive
12
13
 
13
14
  __all__ = [
14
15
  'mcp_server',
@@ -31,4 +32,5 @@ __all__ = [
31
32
  'login_cli',
32
33
  'login',
33
34
  'main',
35
+ 'search_ads_archive',
34
36
  ]
meta_ads_mcp/core/ads.py CHANGED
@@ -5,6 +5,8 @@ from typing import Optional, Dict, Any, List
5
5
  import io
6
6
  from PIL import Image as PILImage
7
7
  from mcp.server.fastmcp import Image
8
+ import os
9
+ import time
8
10
 
9
11
  from .api import meta_api_tool, make_api_request
10
12
  from .accounts import get_ad_accounts
@@ -317,6 +319,132 @@ async def get_ad_image(access_token: str = None, ad_id: str = None) -> Image:
317
319
  return f"Error processing image: {str(e)}"
318
320
 
319
321
 
322
+ @mcp_server.tool()
323
+ @meta_api_tool
324
+ async def save_ad_image_locally(access_token: str = None, ad_id: str = None, output_dir: str = "ad_images") -> str:
325
+ """
326
+ Get, download, and save a Meta ad image locally, returning the file path.
327
+
328
+ Args:
329
+ access_token: Meta API access token (optional - will use cached token if not provided)
330
+ ad_id: Meta Ads ad ID
331
+ output_dir: Directory to save the image file (default: 'ad_images')
332
+
333
+ Returns:
334
+ The file path to the saved image, or an error message string.
335
+ """
336
+ if not ad_id:
337
+ return json.dumps({"error": "No ad ID provided"}, indent=2)
338
+
339
+ print(f"Attempting to get and save creative image for ad {ad_id}")
340
+
341
+ # First, get creative and account IDs
342
+ ad_endpoint = f"{ad_id}"
343
+ ad_params = {
344
+ "fields": "creative{id},account_id"
345
+ }
346
+
347
+ ad_data = await make_api_request(ad_endpoint, access_token, ad_params)
348
+
349
+ if "error" in ad_data:
350
+ return json.dumps({"error": f"Could not get ad data - {json.dumps(ad_data)}"}, indent=2)
351
+
352
+ account_id = ad_data.get("account_id")
353
+ if not account_id:
354
+ return json.dumps({"error": "No account ID found for ad"}, indent=2)
355
+
356
+ if "creative" not in ad_data:
357
+ return json.dumps({"error": "No creative found for this ad"}, indent=2)
358
+
359
+ creative_data = ad_data.get("creative", {})
360
+ creative_id = creative_data.get("id")
361
+ if not creative_id:
362
+ return json.dumps({"error": "No creative ID found"}, indent=2)
363
+
364
+ # Get creative details to find image hash
365
+ creative_endpoint = f"{creative_id}"
366
+ creative_params = {
367
+ "fields": "id,name,image_hash,asset_feed_spec"
368
+ }
369
+ creative_details = await make_api_request(creative_endpoint, access_token, creative_params)
370
+
371
+ image_hashes = []
372
+ if "image_hash" in creative_details:
373
+ image_hashes.append(creative_details["image_hash"])
374
+ if "asset_feed_spec" in creative_details and "images" in creative_details["asset_feed_spec"]:
375
+ for image in creative_details["asset_feed_spec"]["images"]:
376
+ if "hash" in image:
377
+ image_hashes.append(image["hash"])
378
+
379
+ if not image_hashes:
380
+ # Fallback attempt (as in get_ad_image)
381
+ creative_json = await get_ad_creatives(ad_id=ad_id, access_token=access_token) # Ensure ad_id is passed correctly
382
+ creative_data_list = json.loads(creative_json)
383
+ if 'data' in creative_data_list and creative_data_list['data']:
384
+ first_creative = creative_data_list['data'][0]
385
+ if 'object_story_spec' in first_creative and 'link_data' in first_creative['object_story_spec'] and 'image_hash' in first_creative['object_story_spec']['link_data']:
386
+ image_hashes.append(first_creative['object_story_spec']['link_data']['image_hash'])
387
+ elif 'image_hash' in first_creative: # Check direct hash on creative data
388
+ image_hashes.append(first_creative['image_hash'])
389
+
390
+
391
+ if not image_hashes:
392
+ return json.dumps({"error": "No image hashes found in creative or fallback"}, indent=2)
393
+
394
+ print(f"Found image hashes: {image_hashes}")
395
+
396
+ # Fetch image data using the first hash
397
+ image_endpoint = f"act_{account_id}/adimages"
398
+ hashes_str = f'["{image_hashes[0]}"]'
399
+ image_params = {
400
+ "fields": "hash,url,width,height,name,status",
401
+ "hashes": hashes_str
402
+ }
403
+
404
+ print(f"Requesting image data with params: {image_params}")
405
+ image_data = await make_api_request(image_endpoint, access_token, image_params)
406
+
407
+ if "error" in image_data:
408
+ return json.dumps({"error": f"Failed to get image data - {json.dumps(image_data)}"}, indent=2)
409
+
410
+ if "data" not in image_data or not image_data["data"]:
411
+ return json.dumps({"error": "No image data returned from API"}, indent=2)
412
+
413
+ first_image = image_data["data"][0]
414
+ image_url = first_image.get("url")
415
+
416
+ if not image_url:
417
+ return json.dumps({"error": "No valid image URL found in API response"}, indent=2)
418
+
419
+ print(f"Downloading image from URL: {image_url}")
420
+
421
+ # Download and Save Image
422
+ image_bytes = await download_image(image_url)
423
+
424
+ if not image_bytes:
425
+ return json.dumps({"error": "Failed to download image"}, indent=2)
426
+
427
+ try:
428
+ # Ensure output directory exists
429
+ if not os.path.exists(output_dir):
430
+ os.makedirs(output_dir)
431
+
432
+ # Create a filename (e.g., using ad_id and image hash)
433
+ file_extension = ".jpg" # Default extension, could try to infer from headers later
434
+ filename = f"{ad_id}_{image_hashes[0]}{file_extension}"
435
+ filepath = os.path.join(output_dir, filename)
436
+
437
+ # Save the image bytes to the file
438
+ with open(filepath, "wb") as f:
439
+ f.write(image_bytes)
440
+
441
+ print(f"Image saved successfully to: {filepath}")
442
+ return json.dumps({"filepath": filepath}, indent=2) # Return JSON with filepath
443
+
444
+ except Exception as e:
445
+ return json.dumps({"error": f"Failed to save image: {str(e)}"}, indent=2)
446
+
447
+
320
448
  @mcp_server.tool()
321
449
  @meta_api_tool
322
450
  async def update_ad(
@@ -354,4 +482,338 @@ async def update_ad(
354
482
  endpoint = f"{ad_id}"
355
483
  data = await make_api_request(endpoint, access_token, params, method='POST')
356
484
 
357
- return json.dumps(data, indent=2)
485
+ return json.dumps(data, indent=2)
486
+
487
+
488
+ @mcp_server.tool()
489
+ @meta_api_tool
490
+ async def upload_ad_image(
491
+ access_token: str = None,
492
+ account_id: str = None,
493
+ image_path: str = None,
494
+ name: str = None
495
+ ) -> str:
496
+ """
497
+ Upload an image to use in Meta Ads creatives.
498
+
499
+ Args:
500
+ access_token: Meta API access token (optional - will use cached token if not provided)
501
+ account_id: Meta Ads account ID (format: act_XXXXXXXXX)
502
+ image_path: Path to the image file to upload
503
+ name: Optional name for the image (default: filename)
504
+
505
+ Returns:
506
+ JSON response with image details including hash for creative creation
507
+ """
508
+ # Check required parameters
509
+ if not account_id:
510
+ return json.dumps({"error": "No account ID provided"}, indent=2)
511
+
512
+ if not image_path:
513
+ return json.dumps({"error": "No image path provided"}, indent=2)
514
+
515
+ # Ensure account_id has the 'act_' prefix for API compatibility
516
+ if not account_id.startswith("act_"):
517
+ account_id = f"act_{account_id}"
518
+
519
+ # Check if image file exists
520
+ if not os.path.exists(image_path):
521
+ return json.dumps({"error": f"Image file not found: {image_path}"}, indent=2)
522
+
523
+ try:
524
+ # Read image file
525
+ with open(image_path, "rb") as img_file:
526
+ image_bytes = img_file.read()
527
+
528
+ # Get image filename if name not provided
529
+ if not name:
530
+ name = os.path.basename(image_path)
531
+
532
+ # Prepare the API endpoint for uploading images
533
+ endpoint = f"{account_id}/adimages"
534
+
535
+ # We need to convert the binary data to base64 for API upload
536
+ import base64
537
+ encoded_image = base64.b64encode(image_bytes).decode('utf-8')
538
+
539
+ # Prepare POST parameters
540
+ params = {
541
+ "bytes": encoded_image,
542
+ "name": name
543
+ }
544
+
545
+ # Make API request to upload the image
546
+ print(f"Uploading image to Facebook Ad Account {account_id}")
547
+ data = await make_api_request(endpoint, access_token, params, method="POST")
548
+
549
+ return json.dumps(data, indent=2)
550
+
551
+ except Exception as e:
552
+ return json.dumps({
553
+ "error": "Failed to upload image",
554
+ "details": str(e)
555
+ }, indent=2)
556
+
557
+
558
+ @mcp_server.tool()
559
+ @meta_api_tool
560
+ async def create_ad_creative(
561
+ access_token: str = None,
562
+ account_id: str = None,
563
+ name: str = None,
564
+ image_hash: str = None,
565
+ page_id: str = None,
566
+ link_url: str = None,
567
+ message: str = None,
568
+ headline: str = None,
569
+ description: str = None,
570
+ call_to_action_type: str = None,
571
+ instagram_actor_id: str = None
572
+ ) -> str:
573
+ """
574
+ Create a new ad creative using an uploaded image hash.
575
+
576
+ Args:
577
+ access_token: Meta API access token (optional - will use cached token if not provided)
578
+ account_id: Meta Ads account ID (format: act_XXXXXXXXX)
579
+ name: Creative name
580
+ image_hash: Hash of the uploaded image
581
+ page_id: Facebook Page ID to be used for the ad
582
+ link_url: Destination URL for the ad
583
+ message: Ad copy/text
584
+ headline: Ad headline
585
+ description: Ad description
586
+ call_to_action_type: Call to action button type (e.g., 'LEARN_MORE', 'SIGN_UP', 'SHOP_NOW')
587
+ instagram_actor_id: Optional Instagram account ID for Instagram placements
588
+
589
+ Returns:
590
+ JSON response with created creative details
591
+ """
592
+ # Check required parameters
593
+ if not account_id:
594
+ return json.dumps({"error": "No account ID provided"}, indent=2)
595
+
596
+ if not image_hash:
597
+ return json.dumps({"error": "No image hash provided"}, indent=2)
598
+
599
+ if not name:
600
+ name = f"Creative {int(time.time())}"
601
+
602
+ # Ensure account_id has the 'act_' prefix
603
+ if not account_id.startswith("act_"):
604
+ account_id = f"act_{account_id}"
605
+
606
+ # If no page ID is provided, try to find a page associated with the account
607
+ if not page_id:
608
+ try:
609
+ # Query to get pages associated with the account
610
+ pages_endpoint = f"{account_id}/assigned_pages"
611
+ pages_params = {
612
+ "fields": "id,name",
613
+ "limit": 1
614
+ }
615
+
616
+ pages_data = await make_api_request(pages_endpoint, access_token, pages_params)
617
+
618
+ if "data" in pages_data and pages_data["data"]:
619
+ page_id = pages_data["data"][0]["id"]
620
+ print(f"Using page ID: {page_id} ({pages_data['data'][0].get('name', 'Unknown')})")
621
+ else:
622
+ return json.dumps({
623
+ "error": "No page ID provided and no pages found for this account",
624
+ "suggestion": "Please provide a page_id parameter"
625
+ }, indent=2)
626
+ except Exception as e:
627
+ return json.dumps({
628
+ "error": "Error finding page for account",
629
+ "details": str(e),
630
+ "suggestion": "Please provide a page_id parameter"
631
+ }, indent=2)
632
+
633
+ # Prepare the creative data
634
+ creative_data = {
635
+ "name": name,
636
+ "object_story_spec": {
637
+ "page_id": page_id,
638
+ "link_data": {
639
+ "image_hash": image_hash,
640
+ "link": link_url if link_url else "https://facebook.com"
641
+ }
642
+ }
643
+ }
644
+
645
+ # Add optional parameters if provided
646
+ if message:
647
+ creative_data["object_story_spec"]["link_data"]["message"] = message
648
+
649
+ if headline:
650
+ creative_data["object_story_spec"]["link_data"]["name"] = headline
651
+
652
+ if description:
653
+ creative_data["object_story_spec"]["link_data"]["description"] = description
654
+
655
+ if call_to_action_type:
656
+ creative_data["object_story_spec"]["link_data"]["call_to_action"] = {
657
+ "type": call_to_action_type
658
+ }
659
+
660
+ if instagram_actor_id:
661
+ creative_data["instagram_actor_id"] = instagram_actor_id
662
+
663
+ # Prepare the API endpoint for creating a creative
664
+ endpoint = f"{account_id}/adcreatives"
665
+
666
+ try:
667
+ # Make API request to create the creative
668
+ data = await make_api_request(endpoint, access_token, creative_data, method="POST")
669
+
670
+ # If successful, get more details about the created creative
671
+ if "id" in data:
672
+ creative_id = data["id"]
673
+ creative_endpoint = f"{creative_id}"
674
+ creative_params = {
675
+ "fields": "id,name,status,thumbnail_url,image_url,image_hash,object_story_spec,url_tags,link_url"
676
+ }
677
+
678
+ creative_details = await make_api_request(creative_endpoint, access_token, creative_params)
679
+ return json.dumps({
680
+ "success": True,
681
+ "creative_id": creative_id,
682
+ "details": creative_details
683
+ }, indent=2)
684
+
685
+ return json.dumps(data, indent=2)
686
+
687
+ except Exception as e:
688
+ return json.dumps({
689
+ "error": "Failed to create ad creative",
690
+ "details": str(e),
691
+ "creative_data_sent": creative_data
692
+ }, indent=2)
693
+
694
+
695
+ @mcp_server.tool()
696
+ @meta_api_tool
697
+ async def get_account_pages(access_token: str = None, account_id: str = None) -> str:
698
+ """
699
+ Get pages associated with a Meta Ads account.
700
+
701
+ Args:
702
+ access_token: Meta API access token (optional - will use cached token if not provided)
703
+ account_id: Meta Ads account ID (format: act_XXXXXXXXX)
704
+
705
+ Returns:
706
+ JSON response with pages associated with the account
707
+ """
708
+ # Check required parameters
709
+ if not account_id:
710
+ return json.dumps({"error": "No account ID provided"}, indent=2)
711
+
712
+ # Handle special case for 'me'
713
+ if account_id == "me":
714
+ try:
715
+ endpoint = "me/accounts"
716
+ params = {
717
+ "fields": "id,name,username,category,fan_count,link,verification_status,picture"
718
+ }
719
+
720
+ user_pages_data = await make_api_request(endpoint, access_token, params)
721
+ return json.dumps(user_pages_data, indent=2)
722
+ except Exception as e:
723
+ return json.dumps({
724
+ "error": "Failed to get user pages",
725
+ "details": str(e)
726
+ }, indent=2)
727
+
728
+ # Ensure account_id has the 'act_' prefix for regular accounts
729
+ if not account_id.startswith("act_"):
730
+ account_id = f"act_{account_id}"
731
+
732
+ try:
733
+ # Try all approaches that might work
734
+
735
+ # Approach 1: Get active ads and extract page IDs
736
+ endpoint = f"{account_id}/ads"
737
+ params = {
738
+ "fields": "creative{object_story_spec{page_id}}",
739
+ "limit": 100
740
+ }
741
+
742
+ ads_data = await make_api_request(endpoint, access_token, params)
743
+
744
+ # Extract unique page IDs from ads
745
+ page_ids = set()
746
+ if "data" in ads_data:
747
+ for ad in ads_data.get("data", []):
748
+ if "creative" in ad and "creative" in ad and "object_story_spec" in ad["creative"] and "page_id" in ad["creative"]["object_story_spec"]:
749
+ page_ids.add(ad["creative"]["object_story_spec"]["page_id"])
750
+
751
+ # If we found page IDs, get details for each
752
+ if page_ids:
753
+ page_details = {"data": []}
754
+
755
+ for page_id in page_ids:
756
+ page_endpoint = f"{page_id}"
757
+ page_params = {
758
+ "fields": "id,name,username,category,fan_count,link,verification_status,picture"
759
+ }
760
+
761
+ page_data = await make_api_request(page_endpoint, access_token, page_params)
762
+ if "id" in page_data:
763
+ page_details["data"].append(page_data)
764
+
765
+ if page_details["data"]:
766
+ return json.dumps(page_details, indent=2)
767
+
768
+ # Approach 2: Try client_pages endpoint
769
+ endpoint = f"{account_id}/client_pages"
770
+ params = {
771
+ "fields": "id,name,username,category,fan_count,link,verification_status,picture"
772
+ }
773
+
774
+ client_pages_data = await make_api_request(endpoint, access_token, params)
775
+
776
+ if "data" in client_pages_data and client_pages_data["data"]:
777
+ return json.dumps(client_pages_data, indent=2)
778
+
779
+ # Approach 3: Try promoted_objects endpoint to find page IDs
780
+ endpoint = f"{account_id}/promoted_objects"
781
+ params = {
782
+ "fields": "page_id"
783
+ }
784
+
785
+ promoted_objects_data = await make_api_request(endpoint, access_token, params)
786
+
787
+ if "data" in promoted_objects_data and promoted_objects_data["data"]:
788
+ page_ids = set()
789
+ for obj in promoted_objects_data["data"]:
790
+ if "page_id" in obj:
791
+ page_ids.add(obj["page_id"])
792
+
793
+ if page_ids:
794
+ page_details = {"data": []}
795
+ for page_id in page_ids:
796
+ page_endpoint = f"{page_id}"
797
+ page_params = {
798
+ "fields": "id,name,username,category,fan_count,link,verification_status,picture"
799
+ }
800
+
801
+ page_data = await make_api_request(page_endpoint, access_token, page_params)
802
+ if "id" in page_data:
803
+ page_details["data"].append(page_data)
804
+
805
+ if page_details["data"]:
806
+ return json.dumps(page_details, indent=2)
807
+
808
+ # If all approaches failed, return empty data with a message
809
+ return json.dumps({
810
+ "data": [],
811
+ "message": "No pages found associated with this account",
812
+ "suggestion": "You may need to create a page or provide a page_id explicitly when creating ads"
813
+ }, indent=2)
814
+
815
+ except Exception as e:
816
+ return json.dumps({
817
+ "error": "Failed to get account pages",
818
+ "details": str(e)
819
+ }, indent=2)
@@ -0,0 +1,69 @@
1
+ """Adds Library-related functionality for Meta Ads API."""
2
+
3
+ import json
4
+ from typing import Optional, List, Dict, Any
5
+ from .api import meta_api_tool, make_api_request
6
+ from .server import mcp_server
7
+
8
+
9
+ @mcp_server.tool()
10
+ @meta_api_tool
11
+ async def search_ads_archive(
12
+ access_token: str = None,
13
+ search_terms: str = None,
14
+ ad_type: str = "ALL",
15
+ ad_reached_countries: List[str] = None,
16
+ limit: int = 25, # Default limit, adjust as needed
17
+ fields: str = "ad_creation_time,ad_creative_body,ad_creative_link_caption,ad_creative_link_description,ad_creative_link_title,ad_delivery_start_time,ad_delivery_stop_time,ad_snapshot_url,currency,demographic_distribution,funding_entity,impressions,page_id,page_name,publisher_platform,region_distribution,spend"
18
+ ) -> str:
19
+ """
20
+ Search the Facebook Ads Library archive.
21
+
22
+ Args:
23
+ access_token: Meta API access token (optional - will use cached token if not provided).
24
+ search_terms: The search query for ads.
25
+ ad_type: Type of ads to search for (e.g., POLITICAL_AND_ISSUE_ADS, HOUSING_ADS, ALL).
26
+ ad_reached_countries: List of country codes (e.g., ["US", "GB"]).
27
+ limit: Maximum number of ads to return.
28
+ fields: Comma-separated string of fields to retrieve for each ad.
29
+
30
+ Example Usage via curl equivalent:
31
+ curl -G \\
32
+ -d "search_terms='california'" \\
33
+ -d "ad_type=POLITICAL_AND_ISSUE_ADS" \\
34
+ -d "ad_reached_countries=['US']" \\
35
+ -d "fields=ad_snapshot_url,spend" \\
36
+ -d "access_token=<ACCESS_TOKEN>" \\
37
+ "https://graph.facebook.com/<API_VERSION>/ads_archive"
38
+ """
39
+ if not access_token:
40
+ # Attempt to get token implicitly if not provided - meta_api_tool handles this
41
+ pass
42
+
43
+ if not search_terms:
44
+ return json.dumps({"error": "search_terms parameter is required"}, indent=2)
45
+
46
+ if not ad_reached_countries:
47
+ return json.dumps({"error": "ad_reached_countries parameter is required"}, indent=2)
48
+
49
+ endpoint = "ads_archive"
50
+ params = {
51
+ "search_terms": search_terms,
52
+ "ad_type": ad_type,
53
+ "ad_reached_countries": json.dumps(ad_reached_countries), # API expects a JSON array string
54
+ "limit": limit,
55
+ "fields": fields,
56
+ }
57
+
58
+ try:
59
+ data = await make_api_request(endpoint, access_token, params, method="GET")
60
+ return json.dumps(data, indent=2)
61
+ except Exception as e:
62
+ error_msg = str(e)
63
+ # Consider logging the full error for debugging
64
+ # print(f"Error calling Ads Library API: {error_msg}")
65
+ return json.dumps({
66
+ "error": "Failed to search ads archive",
67
+ "details": error_msg,
68
+ "params_sent": {k: v for k, v in params.items() if k != 'access_token'} # Avoid logging token
69
+ }, indent=2)
@@ -23,7 +23,9 @@ async def get_campaigns(access_token: str = None, account_id: str = None, limit:
23
23
  access_token: Meta API access token (optional - will use cached token if not provided)
24
24
  account_id: Meta Ads account ID (format: act_XXXXXXXXX)
25
25
  limit: Maximum number of campaigns to return (default: 10)
26
- status_filter: Filter by status (empty for all, or 'ACTIVE', 'PAUSED', etc.)
26
+ status_filter: Filter by effective status (e.g., 'ACTIVE', 'PAUSED', 'ARCHIVED').
27
+ Maps to the 'effective_status' API parameter, which expects an array
28
+ (this function handles the required JSON formatting). Leave empty for all statuses.
27
29
  """
28
30
  # If no account ID is specified, try to get the first one for the user
29
31
  if not account_id:
@@ -42,7 +44,8 @@ async def get_campaigns(access_token: str = None, account_id: str = None, limit:
42
44
  }
43
45
 
44
46
  if status_filter:
45
- params["effective_status"] = status_filter
47
+ # API expects an array, encode it as a JSON string
48
+ params["effective_status"] = json.dumps([status_filter])
46
49
 
47
50
  data = await make_api_request(endpoint, access_token, params)
48
51
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: meta-ads-mcp
3
- Version: 0.3.2
3
+ Version: 0.3.3
4
4
  Summary: Model Calling Protocol (MCP) plugin for interacting with Meta Ads API
5
5
  Project-URL: Homepage, https://github.com/nictuku/meta-ads-mcp
6
6
  Project-URL: Bug Tracker, https://github.com/nictuku/meta-ads-mcp/issues
@@ -129,7 +129,14 @@ Add this to your `claude_desktop_config.json` to integrate with Claude or `~/.cu
129
129
  - `account_id`: Meta Ads account ID (format: act_XXXXXXXXX)
130
130
  - Returns: Detailed information about the specified account
131
131
 
132
- 3. `mcp_meta_ads_get_campaigns`
132
+ 3. `mcp_meta_ads_get_account_pages`
133
+ - Get pages associated with a Meta Ads account
134
+ - Inputs:
135
+ - `access_token` (optional): Meta API access token (will use cached token if not provided)
136
+ - `account_id`: Meta Ads account ID (format: act_XXXXXXXXX) or "me" for the current user's pages
137
+ - Returns: List of pages associated with the account, useful for ad creation and management
138
+
139
+ 4. `mcp_meta_ads_get_campaigns`
133
140
  - Get campaigns for a Meta Ads account with optional filtering
134
141
  - Inputs:
135
142
  - `access_token` (optional): Meta API access token (will use cached token if not provided)
@@ -138,14 +145,14 @@ Add this to your `claude_desktop_config.json` to integrate with Claude or `~/.cu
138
145
  - `status_filter`: Filter by status (empty for all, or 'ACTIVE', 'PAUSED', etc.)
139
146
  - Returns: List of campaigns matching the criteria
140
147
 
141
- 4. `mcp_meta_ads_get_campaign_details`
148
+ 5. `mcp_meta_ads_get_campaign_details`
142
149
  - Get detailed information about a specific campaign
143
150
  - Inputs:
144
151
  - `access_token` (optional): Meta API access token (will use cached token if not provided)
145
152
  - `campaign_id`: Meta Ads campaign ID
146
153
  - Returns: Detailed information about the specified campaign
147
154
 
148
- 5. `mcp_meta_ads_create_campaign`
155
+ 6. `mcp_meta_ads_create_campaign`
149
156
  - Create a new campaign in a Meta Ads account
150
157
  - Inputs:
151
158
  - `access_token` (optional): Meta API access token (will use cached token if not provided)
@@ -158,7 +165,7 @@ Add this to your `claude_desktop_config.json` to integrate with Claude or `~/.cu
158
165
  - `lifetime_budget`: Lifetime budget in account currency (in cents)
159
166
  - Returns: Confirmation with new campaign details
160
167
 
161
- 6. `mcp_meta_ads_get_adsets`
168
+ 7. `mcp_meta_ads_get_adsets`
162
169
  - Get ad sets for a Meta Ads account with optional filtering by campaign
163
170
  - Inputs:
164
171
  - `access_token` (optional): Meta API access token (will use cached token if not provided)
@@ -167,14 +174,14 @@ Add this to your `claude_desktop_config.json` to integrate with Claude or `~/.cu
167
174
  - `campaign_id`: Optional campaign ID to filter by
168
175
  - Returns: List of ad sets matching the criteria
169
176
 
170
- 7. `mcp_meta_ads_get_adset_details`
177
+ 8. `mcp_meta_ads_get_adset_details`
171
178
  - Get detailed information about a specific ad set
172
179
  - Inputs:
173
180
  - `access_token` (optional): Meta API access token (will use cached token if not provided)
174
181
  - `adset_id`: Meta Ads ad set ID
175
182
  - Returns: Detailed information about the specified ad set
176
183
 
177
- 8. `mcp_meta_ads_get_ads`
184
+ 9. `mcp_meta_ads_get_ads`
178
185
  - Get ads for a Meta Ads account with optional filtering
179
186
  - Inputs:
180
187
  - `access_token` (optional): Meta API access token (will use cached token if not provided)
@@ -184,28 +191,28 @@ Add this to your `claude_desktop_config.json` to integrate with Claude or `~/.cu
184
191
  - `adset_id`: Optional ad set ID to filter by
185
192
  - Returns: List of ads matching the criteria
186
193
 
187
- 9. `mcp_meta_ads_get_ad_details`
188
- - Get detailed information about a specific ad
189
- - Inputs:
190
- - `access_token` (optional): Meta API access token (will use cached token if not provided)
191
- - `ad_id`: Meta Ads ad ID
192
- - Returns: Detailed information about the specified ad
194
+ 10. `mcp_meta_ads_get_ad_details`
195
+ - Get detailed information about a specific ad
196
+ - Inputs:
197
+ - `access_token` (optional): Meta API access token (will use cached token if not provided)
198
+ - `ad_id`: Meta Ads ad ID
199
+ - Returns: Detailed information about the specified ad
193
200
 
194
- 10. `mcp_meta_ads_get_ad_creatives`
201
+ 11. `mcp_meta_ads_get_ad_creatives`
195
202
  - Get creative details for a specific ad
196
203
  - Inputs:
197
204
  - `access_token` (optional): Meta API access token (will use cached token if not provided)
198
205
  - `ad_id`: Meta Ads ad ID
199
206
  - Returns: Creative details including text, images, and URLs
200
207
 
201
- 11. `mcp_meta_ads_get_ad_image`
208
+ 12. `mcp_meta_ads_get_ad_image`
202
209
  - Get, download, and visualize a Meta ad image in one step
203
210
  - Inputs:
204
211
  - `access_token` (optional): Meta API access token (will use cached token if not provided)
205
212
  - `ad_id`: Meta Ads ad ID
206
213
  - Returns: The ad image ready for direct visual analysis
207
214
 
208
- 12. `mcp_meta_ads_update_ad`
215
+ 13. `mcp_meta_ads_update_ad`
209
216
  - Update an ad with new settings
210
217
  - Inputs:
211
218
  - `ad_id`: Meta Ads ad ID
@@ -214,7 +221,7 @@ Add this to your `claude_desktop_config.json` to integrate with Claude or `~/.cu
214
221
  - `access_token` (optional): Meta API access token (will use cached token if not provided)
215
222
  - Returns: Confirmation with updated ad details and a confirmation link
216
223
 
217
- 13. `mcp_meta_ads_update_adset`
224
+ 14. `mcp_meta_ads_update_adset`
218
225
  - Update an ad set with new settings including frequency caps
219
226
  - Inputs:
220
227
  - `adset_id`: Meta Ads ad set ID
@@ -226,7 +233,7 @@ Add this to your `claude_desktop_config.json` to integrate with Claude or `~/.cu
226
233
  - `access_token` (optional): Meta API access token (will use cached token if not provided)
227
234
  - Returns: Confirmation with updated ad set details and a confirmation link
228
235
 
229
- 14. `mcp_meta_ads_get_insights`
236
+ 15. `mcp_meta_ads_get_insights`
230
237
  - Get performance insights for a campaign, ad set, ad or account
231
238
  - Inputs:
232
239
  - `access_token` (optional): Meta API access token (will use cached token if not provided)
@@ -236,7 +243,7 @@ Add this to your `claude_desktop_config.json` to integrate with Claude or `~/.cu
236
243
  - `level`: Level of aggregation (ad, adset, campaign, account)
237
244
  - Returns: Performance metrics for the specified object
238
245
 
239
- 15. `mcp_meta_ads_debug_image_download`
246
+ 16. `mcp_meta_ads_debug_image_download`
240
247
  - Debug image download issues and report detailed diagnostics
241
248
  - Inputs:
242
249
  - `access_token` (optional): Meta API access token (will use cached token if not provided)
@@ -244,7 +251,7 @@ Add this to your `claude_desktop_config.json` to integrate with Claude or `~/.cu
244
251
  - `ad_id`: Meta Ads ad ID (optional, used if url is not provided)
245
252
  - Returns: Diagnostic information about image download attempts
246
253
 
247
- 16. `mcp_meta_ads_get_login_link`
254
+ 17. `mcp_meta_ads_get_login_link`
248
255
  - Get a clickable login link for Meta Ads authentication
249
256
  - Inputs:
250
257
  - `access_token` (optional): Meta API access token (will use cached token if not provided)
@@ -1,22 +1,23 @@
1
- meta_ads_mcp/__init__.py,sha256=Xa5R_8nWJqcRh6Jps90mcuihcp4bJDKGXGg1bwnoSz4,1236
1
+ meta_ads_mcp/__init__.py,sha256=xQxyDpHFTg1FE9C3YEk88za2I8lvr8Mg0h9dMTxc5dw,1236
2
2
  meta_ads_mcp/__main__.py,sha256=XaQt3iXftG_7f0Zu7Wop9SeFgrD2WBn0EQOaPMc27d8,207
3
3
  meta_ads_mcp/api.py,sha256=z0pW1pV3hE75IeG9QTqB3K7QoQOUxUg2MBQ9IjAWUYA,84363
4
- meta_ads_mcp/core/__init__.py,sha256=6T8iqrQrw9VHhKtncLqYWyDk8jeSBPs79hs1CSu-fLU,952
4
+ meta_ads_mcp/core/__init__.py,sha256=MOCvj3SkLvr5hFHqyjWOx7L4oReKLCoZaotwspJLwZQ,1022
5
5
  meta_ads_mcp/core/accounts.py,sha256=Nmp7lPxO9wmq25jWV7_H0LIqnEbBhpCVBlLGW2HUaq0,2277
6
- meta_ads_mcp/core/ads.py,sha256=LMZOo6agi6tQl4JJPmrDUn-91n7DzfxG2TChmwcrWOY,12544
6
+ meta_ads_mcp/core/ads.py,sha256=b_81GlGHIM4jISvuDZmHNyc6uW7uD3ovX68ezBci9MM,29747
7
+ meta_ads_mcp/core/ads_library.py,sha256=onStn9UkRqYDC60gOPS-iKDtP1plz6DygUb7hUZ0Jw8,2807
7
8
  meta_ads_mcp/core/adsets.py,sha256=WBPNaI7ITnUOnGMus4_0MX15DslOCzfM5q1zF1VWs2s,12408
8
9
  meta_ads_mcp/core/api.py,sha256=9Whcs2orILhPiWkAR3qGmJNouYE5uri_e_Jzeh5Hjn8,14208
9
10
  meta_ads_mcp/core/auth.py,sha256=pDARBh3NBNqCpxflVrVvR4VsWuIveFxQmb9-P-gLFDM,20730
10
11
  meta_ads_mcp/core/authentication.py,sha256=2MG13r28OlIcOIgPSRrGXJ2-4JSt3ifU-oB9tiOsrKQ,6511
11
12
  meta_ads_mcp/core/callback_server.py,sha256=AUymElaVwHqFyqB2wgqf6A68KsqwtKoYmY-7JZZt8Ks,43286
12
- meta_ads_mcp/core/campaigns.py,sha256=20DHMwHppZuoZBg0owkf1BfmPBhWzkQcFgh5rQa-pAU,10480
13
+ meta_ads_mcp/core/campaigns.py,sha256=TQHDhJ0s7cLbo5-zd2Bk8YgwToWBoK3YBMFG8fZbEHI,10757
13
14
  meta_ads_mcp/core/insights.py,sha256=XAm4uu83gWp84PEGqAJ3GFIqlvg7prh6MdD71JfvBCo,18072
14
15
  meta_ads_mcp/core/pipeboard_auth.py,sha256=VvbxEB8ZOhnMccLU7HI1HgaPWHCl5NGrzZCm-zzHze4,22798
15
16
  meta_ads_mcp/core/resources.py,sha256=-zIIfZulpo76vcKv6jhAlQq91cR2SZ3cjYZt3ek3x0w,1236
16
17
  meta_ads_mcp/core/server.py,sha256=5WofyJZGzeDhbGzLXPhQjT0XnZwo0syeK8TM_XnJo4Q,5507
17
18
  meta_ads_mcp/core/utils.py,sha256=EPmpBX3OZaTWRS_YuEk_PLLyLXj7DeR6Ks8WoaZ5JGQ,6366
18
- meta_ads_mcp-0.3.2.dist-info/METADATA,sha256=Q_05xZMow3h9ova-mvfiL210rWvy-Ix9B_sZEetjN4k,16212
19
- meta_ads_mcp-0.3.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
20
- meta_ads_mcp-0.3.2.dist-info/entry_points.txt,sha256=Dv2RkoBjRJBqj6CyhwqGIiwPCD-SCL1-7B9-zmVRuv0,57
21
- meta_ads_mcp-0.3.2.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
22
- meta_ads_mcp-0.3.2.dist-info/RECORD,,
19
+ meta_ads_mcp-0.3.3.dist-info/METADATA,sha256=C1jgrXLiHpq2uRgTp259f2B639FevzezEextSF_xyUc,16611
20
+ meta_ads_mcp-0.3.3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
21
+ meta_ads_mcp-0.3.3.dist-info/entry_points.txt,sha256=Dv2RkoBjRJBqj6CyhwqGIiwPCD-SCL1-7B9-zmVRuv0,57
22
+ meta_ads_mcp-0.3.3.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
23
+ meta_ads_mcp-0.3.3.dist-info/RECORD,,