meta-ads-mcp 0.3.2__py3-none-any.whl → 0.3.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 +1 -1
- meta_ads_mcp/core/__init__.py +4 -0
- meta_ads_mcp/core/ads.py +463 -1
- meta_ads_mcp/core/ads_library.py +69 -0
- meta_ads_mcp/core/budget_schedules.py +71 -0
- meta_ads_mcp/core/campaigns.py +5 -2
- {meta_ads_mcp-0.3.2.dist-info → meta_ads_mcp-0.3.5.dist-info}/METADATA +104 -34
- {meta_ads_mcp-0.3.2.dist-info → meta_ads_mcp-0.3.5.dist-info}/RECORD +11 -9
- {meta_ads_mcp-0.3.2.dist-info → meta_ads_mcp-0.3.5.dist-info}/WHEEL +0 -0
- {meta_ads_mcp-0.3.2.dist-info → meta_ads_mcp-0.3.5.dist-info}/entry_points.txt +0 -0
- {meta_ads_mcp-0.3.2.dist-info → meta_ads_mcp-0.3.5.dist-info}/licenses/LICENSE +0 -0
meta_ads_mcp/__init__.py
CHANGED
meta_ads_mcp/core/__init__.py
CHANGED
|
@@ -9,6 +9,8 @@ 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
|
|
13
|
+
from .budget_schedules import create_budget_schedule
|
|
12
14
|
|
|
13
15
|
__all__ = [
|
|
14
16
|
'mcp_server',
|
|
@@ -31,4 +33,6 @@ __all__ = [
|
|
|
31
33
|
'login_cli',
|
|
32
34
|
'login',
|
|
33
35
|
'main',
|
|
36
|
+
'search_ads_archive',
|
|
37
|
+
'create_budget_schedule',
|
|
34
38
|
]
|
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)
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"""Budget Schedule-related functionality for Meta Ads API."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from typing import Optional, Dict, Any
|
|
5
|
+
|
|
6
|
+
from .api import meta_api_tool, make_api_request
|
|
7
|
+
from .server import mcp_server
|
|
8
|
+
# Assuming no other specific dependencies from adsets.py are needed for this single function.
|
|
9
|
+
# If other utilities from adsets.py (like get_ad_accounts) were needed, they'd be imported here.
|
|
10
|
+
|
|
11
|
+
@mcp_server.tool()
|
|
12
|
+
@meta_api_tool
|
|
13
|
+
async def create_budget_schedule(
|
|
14
|
+
campaign_id: str,
|
|
15
|
+
budget_value: int,
|
|
16
|
+
budget_value_type: str,
|
|
17
|
+
time_start: int,
|
|
18
|
+
time_end: int,
|
|
19
|
+
access_token: str = None
|
|
20
|
+
) -> str:
|
|
21
|
+
"""
|
|
22
|
+
Create a budget schedule for a Meta Ads campaign.
|
|
23
|
+
|
|
24
|
+
Allows scheduling budget increases based on anticipated high-demand periods.
|
|
25
|
+
The times should be provided as Unix timestamps.
|
|
26
|
+
|
|
27
|
+
Args:
|
|
28
|
+
campaign_id: Meta Ads campaign ID.
|
|
29
|
+
budget_value: Amount of budget increase. Interpreted based on budget_value_type.
|
|
30
|
+
budget_value_type: Type of budget value - "ABSOLUTE" or "MULTIPLIER".
|
|
31
|
+
time_start: Unix timestamp for when the high demand period should start.
|
|
32
|
+
time_end: Unix timestamp for when the high demand period should end.
|
|
33
|
+
access_token: Meta API access token (optional - will use cached token if not provided).
|
|
34
|
+
|
|
35
|
+
Returns:
|
|
36
|
+
A JSON string containing the ID of the created budget schedule or an error message.
|
|
37
|
+
"""
|
|
38
|
+
if not campaign_id:
|
|
39
|
+
return json.dumps({"error": "Campaign ID is required"}, indent=2)
|
|
40
|
+
if budget_value is None: # Check for None explicitly
|
|
41
|
+
return json.dumps({"error": "Budget value is required"}, indent=2)
|
|
42
|
+
if not budget_value_type:
|
|
43
|
+
return json.dumps({"error": "Budget value type is required"}, indent=2)
|
|
44
|
+
if budget_value_type not in ["ABSOLUTE", "MULTIPLIER"]:
|
|
45
|
+
return json.dumps({"error": "Invalid budget_value_type. Must be ABSOLUTE or MULTIPLIER"}, indent=2)
|
|
46
|
+
if time_start is None: # Check for None explicitly to allow 0
|
|
47
|
+
return json.dumps({"error": "Time start is required"}, indent=2)
|
|
48
|
+
if time_end is None: # Check for None explicitly to allow 0
|
|
49
|
+
return json.dumps({"error": "Time end is required"}, indent=2)
|
|
50
|
+
|
|
51
|
+
endpoint = f"{campaign_id}/budget_schedules"
|
|
52
|
+
|
|
53
|
+
params = {
|
|
54
|
+
"budget_value": budget_value,
|
|
55
|
+
"budget_value_type": budget_value_type,
|
|
56
|
+
"time_start": time_start,
|
|
57
|
+
"time_end": time_end,
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
try:
|
|
61
|
+
data = await make_api_request(endpoint, access_token, params, method="POST")
|
|
62
|
+
return json.dumps(data, indent=2)
|
|
63
|
+
except Exception as e:
|
|
64
|
+
error_msg = str(e)
|
|
65
|
+
# Include details about the error and the parameters sent for easier debugging
|
|
66
|
+
return json.dumps({
|
|
67
|
+
"error": "Failed to create budget schedule",
|
|
68
|
+
"details": error_msg,
|
|
69
|
+
"campaign_id": campaign_id,
|
|
70
|
+
"params_sent": params
|
|
71
|
+
}, indent=2)
|
meta_ads_mcp/core/campaigns.py
CHANGED
|
@@ -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 (
|
|
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
|
-
|
|
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.
|
|
3
|
+
Version: 0.3.5
|
|
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
|
|
@@ -11,7 +11,7 @@ Keywords: ads,api,claude,facebook,mcp,meta
|
|
|
11
11
|
Classifier: License :: OSI Approved :: MIT License
|
|
12
12
|
Classifier: Operating System :: OS Independent
|
|
13
13
|
Classifier: Programming Language :: Python :: 3
|
|
14
|
-
Requires-Python: >=3.
|
|
14
|
+
Requires-Python: >=3.9.6
|
|
15
15
|
Requires-Dist: httpx>=0.26.0
|
|
16
16
|
Requires-Dist: mcp[cli]>=1.6.0
|
|
17
17
|
Requires-Dist: pathlib>=1.0.1
|
|
@@ -27,11 +27,7 @@ A [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) server for in
|
|
|
27
27
|
|
|
28
28
|
> **DISCLAIMER:** This is an unofficial third-party tool and is not associated with, endorsed by, or affiliated with Meta in any way. This project is maintained independently and uses Meta's public APIs according to their terms of service. Meta, Facebook, Instagram, and other Meta brand names are trademarks of their respective owners.
|
|
29
29
|
|
|
30
|
-
|
|
31
|
-
<img width="380" height="200" src="https://glama.ai/mcp/servers/@pipeboard-co/meta-ads-mcp/badge" alt="Meta Ads MCP server" />
|
|
32
|
-
</a>
|
|
33
|
-
|
|
34
|
-
Screenshot: using an LLM to understand your ad performance.
|
|
30
|
+
**Screenshot**: Using an LLM to understand your ad performance:
|
|
35
31
|
|
|
36
32
|

|
|
37
33
|
|
|
@@ -94,7 +90,7 @@ python -m meta_ads_mcp --app-id YOUR_META_ADS_APP_ID
|
|
|
94
90
|
Before using the MCP server, you'll need to set up a Meta Developer App:
|
|
95
91
|
|
|
96
92
|
1. Go to [Meta for Developers](https://developers.facebook.com/) and create a new app
|
|
97
|
-
2. Choose the "
|
|
93
|
+
2. Choose the "Business" app type
|
|
98
94
|
3. In your app settings, add the "Marketing API" product
|
|
99
95
|
4. Configure your app's OAuth redirect URI to include `http://localhost:8888/callback`
|
|
100
96
|
5. Note your App ID (Client ID) for use with the MCP
|
|
@@ -129,7 +125,14 @@ Add this to your `claude_desktop_config.json` to integrate with Claude or `~/.cu
|
|
|
129
125
|
- `account_id`: Meta Ads account ID (format: act_XXXXXXXXX)
|
|
130
126
|
- Returns: Detailed information about the specified account
|
|
131
127
|
|
|
132
|
-
3. `
|
|
128
|
+
3. `mcp_meta_ads_get_account_pages`
|
|
129
|
+
- Get pages associated with a Meta Ads account
|
|
130
|
+
- Inputs:
|
|
131
|
+
- `access_token` (optional): Meta API access token (will use cached token if not provided)
|
|
132
|
+
- `account_id`: Meta Ads account ID (format: act_XXXXXXXXX) or "me" for the current user's pages
|
|
133
|
+
- Returns: List of pages associated with the account, useful for ad creation and management
|
|
134
|
+
|
|
135
|
+
4. `mcp_meta_ads_get_campaigns`
|
|
133
136
|
- Get campaigns for a Meta Ads account with optional filtering
|
|
134
137
|
- Inputs:
|
|
135
138
|
- `access_token` (optional): Meta API access token (will use cached token if not provided)
|
|
@@ -138,14 +141,14 @@ Add this to your `claude_desktop_config.json` to integrate with Claude or `~/.cu
|
|
|
138
141
|
- `status_filter`: Filter by status (empty for all, or 'ACTIVE', 'PAUSED', etc.)
|
|
139
142
|
- Returns: List of campaigns matching the criteria
|
|
140
143
|
|
|
141
|
-
|
|
144
|
+
5. `mcp_meta_ads_get_campaign_details`
|
|
142
145
|
- Get detailed information about a specific campaign
|
|
143
146
|
- Inputs:
|
|
144
147
|
- `access_token` (optional): Meta API access token (will use cached token if not provided)
|
|
145
148
|
- `campaign_id`: Meta Ads campaign ID
|
|
146
149
|
- Returns: Detailed information about the specified campaign
|
|
147
150
|
|
|
148
|
-
|
|
151
|
+
6. `mcp_meta_ads_create_campaign`
|
|
149
152
|
- Create a new campaign in a Meta Ads account
|
|
150
153
|
- Inputs:
|
|
151
154
|
- `access_token` (optional): Meta API access token (will use cached token if not provided)
|
|
@@ -158,7 +161,7 @@ Add this to your `claude_desktop_config.json` to integrate with Claude or `~/.cu
|
|
|
158
161
|
- `lifetime_budget`: Lifetime budget in account currency (in cents)
|
|
159
162
|
- Returns: Confirmation with new campaign details
|
|
160
163
|
|
|
161
|
-
|
|
164
|
+
7. `mcp_meta_ads_get_adsets`
|
|
162
165
|
- Get ad sets for a Meta Ads account with optional filtering by campaign
|
|
163
166
|
- Inputs:
|
|
164
167
|
- `access_token` (optional): Meta API access token (will use cached token if not provided)
|
|
@@ -167,45 +170,101 @@ Add this to your `claude_desktop_config.json` to integrate with Claude or `~/.cu
|
|
|
167
170
|
- `campaign_id`: Optional campaign ID to filter by
|
|
168
171
|
- Returns: List of ad sets matching the criteria
|
|
169
172
|
|
|
170
|
-
|
|
173
|
+
8. `mcp_meta_ads_get_adset_details`
|
|
171
174
|
- Get detailed information about a specific ad set
|
|
172
175
|
- Inputs:
|
|
173
176
|
- `access_token` (optional): Meta API access token (will use cached token if not provided)
|
|
174
177
|
- `adset_id`: Meta Ads ad set ID
|
|
175
178
|
- Returns: Detailed information about the specified ad set
|
|
176
179
|
|
|
177
|
-
|
|
178
|
-
-
|
|
180
|
+
9. `mcp_meta_ads_create_adset`
|
|
181
|
+
- Create a new ad set in a Meta Ads account
|
|
179
182
|
- Inputs:
|
|
180
|
-
- `access_token` (optional): Meta API access token (will use cached token if not provided)
|
|
181
183
|
- `account_id`: Meta Ads account ID (format: act_XXXXXXXXX)
|
|
182
|
-
- `
|
|
183
|
-
- `
|
|
184
|
-
- `
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
- `
|
|
191
|
-
- `
|
|
192
|
-
|
|
184
|
+
- `campaign_id`: Meta Ads campaign ID this ad set belongs to
|
|
185
|
+
- `name`: Ad set name
|
|
186
|
+
- `status`: Initial ad set status (default: PAUSED)
|
|
187
|
+
- `daily_budget`: Daily budget in account currency (in cents) as a string
|
|
188
|
+
- `lifetime_budget`: Lifetime budget in account currency (in cents) as a string
|
|
189
|
+
- `targeting`: Targeting specifications (e.g., age, location, interests)
|
|
190
|
+
- `optimization_goal`: Conversion optimization goal (e.g., 'LINK_CLICKS')
|
|
191
|
+
- `billing_event`: How you're charged (e.g., 'IMPRESSIONS')
|
|
192
|
+
- `bid_amount`: Bid amount in account currency (in cents)
|
|
193
|
+
- `bid_strategy`: Bid strategy (e.g., 'LOWEST_COST')
|
|
194
|
+
- `start_time`, `end_time`: Optional start/end times (ISO 8601)
|
|
195
|
+
- `access_token` (optional): Meta API access token
|
|
196
|
+
- Returns: Confirmation with new ad set details
|
|
197
|
+
|
|
198
|
+
10. `mcp_meta_ads_get_ads`
|
|
199
|
+
- Get ads for a Meta Ads account with optional filtering
|
|
200
|
+
- Inputs:
|
|
201
|
+
- `access_token` (optional): Meta API access token (will use cached token if not provided)
|
|
202
|
+
- `account_id`: Meta Ads account ID (format: act_XXXXXXXXX)
|
|
203
|
+
- `limit`: Maximum number of ads to return (default: 10)
|
|
204
|
+
- `campaign_id`: Optional campaign ID to filter by
|
|
205
|
+
- `adset_id`: Optional ad set ID to filter by
|
|
206
|
+
- Returns: List of ads matching the criteria
|
|
207
|
+
|
|
208
|
+
11. `mcp_meta_ads_create_ad`
|
|
209
|
+
- Create a new ad with an existing creative
|
|
210
|
+
- Inputs:
|
|
211
|
+
- `account_id`: Meta Ads account ID (format: act_XXXXXXXXX)
|
|
212
|
+
- `name`: Ad name
|
|
213
|
+
- `adset_id`: Ad set ID where this ad will be placed
|
|
214
|
+
- `creative_id`: ID of an existing creative to use
|
|
215
|
+
- `status`: Initial ad status (default: PAUSED)
|
|
216
|
+
- `bid_amount`: Optional bid amount (in cents)
|
|
217
|
+
- `tracking_specs`: Optional tracking specifications
|
|
218
|
+
- `access_token` (optional): Meta API access token
|
|
219
|
+
- Returns: Confirmation with new ad details
|
|
220
|
+
|
|
221
|
+
12. `mcp_meta_ads_get_ad_details`
|
|
222
|
+
- Get detailed information about a specific ad
|
|
223
|
+
- Inputs:
|
|
224
|
+
- `access_token` (optional): Meta API access token (will use cached token if not provided)
|
|
225
|
+
- `ad_id`: Meta Ads ad ID
|
|
226
|
+
- Returns: Detailed information about the specified ad
|
|
193
227
|
|
|
194
|
-
|
|
228
|
+
13. `mcp_meta_ads_get_ad_creatives`
|
|
195
229
|
- Get creative details for a specific ad
|
|
196
230
|
- Inputs:
|
|
197
231
|
- `access_token` (optional): Meta API access token (will use cached token if not provided)
|
|
198
232
|
- `ad_id`: Meta Ads ad ID
|
|
199
233
|
- Returns: Creative details including text, images, and URLs
|
|
200
234
|
|
|
201
|
-
|
|
235
|
+
14. `mcp_meta_ads_create_ad_creative`
|
|
236
|
+
- Create a new ad creative using an uploaded image hash
|
|
237
|
+
- Inputs:
|
|
238
|
+
- `account_id`: Meta Ads account ID (format: act_XXXXXXXXX)
|
|
239
|
+
- `name`: Creative name
|
|
240
|
+
- `image_hash`: Hash of the uploaded image
|
|
241
|
+
- `page_id`: Facebook Page ID for the ad
|
|
242
|
+
- `link_url`: Destination URL
|
|
243
|
+
- `message`: Ad copy/text
|
|
244
|
+
- `headline`: Ad headline
|
|
245
|
+
- `description`: Ad description
|
|
246
|
+
- `call_to_action_type`: CTA button type (e.g., 'LEARN_MORE')
|
|
247
|
+
- `instagram_actor_id`: Optional Instagram account ID
|
|
248
|
+
- `access_token` (optional): Meta API access token
|
|
249
|
+
- Returns: Confirmation with new creative details
|
|
250
|
+
|
|
251
|
+
15. `mcp_meta_ads_upload_ad_image`
|
|
252
|
+
- Upload an image to use in Meta Ads creatives
|
|
253
|
+
- Inputs:
|
|
254
|
+
- `account_id`: Meta Ads account ID (format: act_XXXXXXXXX)
|
|
255
|
+
- `image_path`: Path to the image file to upload
|
|
256
|
+
- `name`: Optional name for the image
|
|
257
|
+
- `access_token` (optional): Meta API access token
|
|
258
|
+
- Returns: JSON response with image details including hash
|
|
259
|
+
|
|
260
|
+
16. `mcp_meta_ads_get_ad_image`
|
|
202
261
|
- Get, download, and visualize a Meta ad image in one step
|
|
203
262
|
- Inputs:
|
|
204
263
|
- `access_token` (optional): Meta API access token (will use cached token if not provided)
|
|
205
264
|
- `ad_id`: Meta Ads ad ID
|
|
206
265
|
- Returns: The ad image ready for direct visual analysis
|
|
207
266
|
|
|
208
|
-
|
|
267
|
+
17. `mcp_meta_ads_update_ad`
|
|
209
268
|
- Update an ad with new settings
|
|
210
269
|
- Inputs:
|
|
211
270
|
- `ad_id`: Meta Ads ad ID
|
|
@@ -214,7 +273,7 @@ Add this to your `claude_desktop_config.json` to integrate with Claude or `~/.cu
|
|
|
214
273
|
- `access_token` (optional): Meta API access token (will use cached token if not provided)
|
|
215
274
|
- Returns: Confirmation with updated ad details and a confirmation link
|
|
216
275
|
|
|
217
|
-
|
|
276
|
+
18. `mcp_meta_ads_update_adset`
|
|
218
277
|
- Update an ad set with new settings including frequency caps
|
|
219
278
|
- Inputs:
|
|
220
279
|
- `adset_id`: Meta Ads ad set ID
|
|
@@ -226,7 +285,7 @@ Add this to your `claude_desktop_config.json` to integrate with Claude or `~/.cu
|
|
|
226
285
|
- `access_token` (optional): Meta API access token (will use cached token if not provided)
|
|
227
286
|
- Returns: Confirmation with updated ad set details and a confirmation link
|
|
228
287
|
|
|
229
|
-
|
|
288
|
+
19. `mcp_meta_ads_get_insights`
|
|
230
289
|
- Get performance insights for a campaign, ad set, ad or account
|
|
231
290
|
- Inputs:
|
|
232
291
|
- `access_token` (optional): Meta API access token (will use cached token if not provided)
|
|
@@ -236,7 +295,7 @@ Add this to your `claude_desktop_config.json` to integrate with Claude or `~/.cu
|
|
|
236
295
|
- `level`: Level of aggregation (ad, adset, campaign, account)
|
|
237
296
|
- Returns: Performance metrics for the specified object
|
|
238
297
|
|
|
239
|
-
|
|
298
|
+
20. `mcp_meta_ads_debug_image_download`
|
|
240
299
|
- Debug image download issues and report detailed diagnostics
|
|
241
300
|
- Inputs:
|
|
242
301
|
- `access_token` (optional): Meta API access token (will use cached token if not provided)
|
|
@@ -244,12 +303,23 @@ Add this to your `claude_desktop_config.json` to integrate with Claude or `~/.cu
|
|
|
244
303
|
- `ad_id`: Meta Ads ad ID (optional, used if url is not provided)
|
|
245
304
|
- Returns: Diagnostic information about image download attempts
|
|
246
305
|
|
|
247
|
-
|
|
306
|
+
21. `mcp_meta_ads_get_login_link`
|
|
248
307
|
- Get a clickable login link for Meta Ads authentication
|
|
249
308
|
- Inputs:
|
|
250
309
|
- `access_token` (optional): Meta API access token (will use cached token if not provided)
|
|
251
310
|
- Returns: A clickable resource link for Meta authentication
|
|
252
311
|
|
|
312
|
+
22. `mcp_meta-ads_create_budget_schedule`
|
|
313
|
+
- Create a budget schedule for a Meta Ads campaign.
|
|
314
|
+
- Inputs:
|
|
315
|
+
- `campaign_id`: Meta Ads campaign ID.
|
|
316
|
+
- `budget_value`: Amount of budget increase.
|
|
317
|
+
- `budget_value_type`: Type of budget value ("ABSOLUTE" or "MULTIPLIER").
|
|
318
|
+
- `time_start`: Unix timestamp for when the high demand period should start.
|
|
319
|
+
- `time_end`: Unix timestamp for when the high demand period should end.
|
|
320
|
+
- `access_token` (optional): Meta API access token.
|
|
321
|
+
- Returns: JSON string with the ID of the created budget schedule or an error message.
|
|
322
|
+
|
|
253
323
|
## Authentication
|
|
254
324
|
|
|
255
325
|
The Meta Ads MCP uses Meta's OAuth 2.0 authentication flow, designed for desktop apps:
|
|
@@ -1,22 +1,24 @@
|
|
|
1
|
-
meta_ads_mcp/__init__.py,sha256=
|
|
1
|
+
meta_ads_mcp/__init__.py,sha256=RSDlsZrR_mt2PklzoAeG_4555QHPTM1ZJrvr5aVadjU,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=
|
|
4
|
+
meta_ads_mcp/core/__init__.py,sha256=NvRA_socbKPEXFXIYdso5jBHb8cEEpF_2Mwhe3Obguw,1105
|
|
5
5
|
meta_ads_mcp/core/accounts.py,sha256=Nmp7lPxO9wmq25jWV7_H0LIqnEbBhpCVBlLGW2HUaq0,2277
|
|
6
|
-
meta_ads_mcp/core/ads.py,sha256=
|
|
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
|
|
12
|
+
meta_ads_mcp/core/budget_schedules.py,sha256=UxseExsvKAiPwfDCY9aycT4kys4xqeNytyq-yyDOxrs,2901
|
|
11
13
|
meta_ads_mcp/core/callback_server.py,sha256=AUymElaVwHqFyqB2wgqf6A68KsqwtKoYmY-7JZZt8Ks,43286
|
|
12
|
-
meta_ads_mcp/core/campaigns.py,sha256=
|
|
14
|
+
meta_ads_mcp/core/campaigns.py,sha256=TQHDhJ0s7cLbo5-zd2Bk8YgwToWBoK3YBMFG8fZbEHI,10757
|
|
13
15
|
meta_ads_mcp/core/insights.py,sha256=XAm4uu83gWp84PEGqAJ3GFIqlvg7prh6MdD71JfvBCo,18072
|
|
14
16
|
meta_ads_mcp/core/pipeboard_auth.py,sha256=VvbxEB8ZOhnMccLU7HI1HgaPWHCl5NGrzZCm-zzHze4,22798
|
|
15
17
|
meta_ads_mcp/core/resources.py,sha256=-zIIfZulpo76vcKv6jhAlQq91cR2SZ3cjYZt3ek3x0w,1236
|
|
16
18
|
meta_ads_mcp/core/server.py,sha256=5WofyJZGzeDhbGzLXPhQjT0XnZwo0syeK8TM_XnJo4Q,5507
|
|
17
19
|
meta_ads_mcp/core/utils.py,sha256=EPmpBX3OZaTWRS_YuEk_PLLyLXj7DeR6Ks8WoaZ5JGQ,6366
|
|
18
|
-
meta_ads_mcp-0.3.
|
|
19
|
-
meta_ads_mcp-0.3.
|
|
20
|
-
meta_ads_mcp-0.3.
|
|
21
|
-
meta_ads_mcp-0.3.
|
|
22
|
-
meta_ads_mcp-0.3.
|
|
20
|
+
meta_ads_mcp-0.3.5.dist-info/METADATA,sha256=lbTJVtr81f-WwPI87zeKUKqGe0HzZrlC5c-WJeZKv2c,19650
|
|
21
|
+
meta_ads_mcp-0.3.5.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
22
|
+
meta_ads_mcp-0.3.5.dist-info/entry_points.txt,sha256=Dv2RkoBjRJBqj6CyhwqGIiwPCD-SCL1-7B9-zmVRuv0,57
|
|
23
|
+
meta_ads_mcp-0.3.5.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
24
|
+
meta_ads_mcp-0.3.5.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|