meta-ads-mcp 0.4.5__py3-none-any.whl → 0.4.7__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.5"
10
+ __version__ = "0.4.7"
11
11
 
12
12
  __all__ = [
13
13
  'get_ad_accounts',
meta_ads_mcp/core/ads.py CHANGED
@@ -10,7 +10,7 @@ import time
10
10
 
11
11
  from .api import meta_api_tool, make_api_request
12
12
  from .accounts import get_ad_accounts
13
- from .utils import download_image, try_multiple_download_methods, ad_creative_images
13
+ from .utils import download_image, try_multiple_download_methods, ad_creative_images, extract_creative_image_urls
14
14
  from .server import mcp_server
15
15
 
16
16
 
@@ -179,7 +179,7 @@ async def get_ad_creatives(access_token: str = None, ad_id: str = None) -> str:
179
179
  # Add image URLs for direct viewing if available
180
180
  if 'data' in data:
181
181
  for creative in data['data']:
182
- creative['image_urls_for_viewing'] = ad_creative_images(creative)
182
+ creative['image_urls_for_viewing'] = extract_creative_image_urls(creative)
183
183
 
184
184
  return json.dumps(data, indent=2)
185
185
 
@@ -806,11 +806,74 @@ async def get_account_pages(access_token: str = None, account_id: str = None) ->
806
806
  if page_details["data"]:
807
807
  return json.dumps(page_details, indent=2)
808
808
 
809
+ # Approach 4: Extract page IDs from tracking_specs in ads
810
+ # Inspired by praveen92y's implementation for robust page detection
811
+ # This approach is often the most reliable as confirmed by community feedback
812
+ endpoint = f"{account_id}/ads"
813
+ params = {
814
+ "fields": "id,name,adset_id,campaign_id,status,creative,created_time,updated_time,bid_amount,conversion_domain,tracking_specs",
815
+ "limit": 100
816
+ }
817
+
818
+ tracking_ads_data = await make_api_request(endpoint, access_token, params)
819
+
820
+ tracking_page_ids = set()
821
+ if "data" in tracking_ads_data:
822
+ for ad in tracking_ads_data.get("data", []):
823
+ tracking_specs = ad.get("tracking_specs", [])
824
+ if isinstance(tracking_specs, list):
825
+ for spec in tracking_specs:
826
+ # If 'page' key exists, add all page IDs
827
+ if isinstance(spec, dict) and "page" in spec:
828
+ page_list = spec["page"]
829
+ if isinstance(page_list, list):
830
+ for page_id in page_list:
831
+ # Validate page ID format (should be numeric string)
832
+ if isinstance(page_id, (str, int)) and str(page_id).isdigit():
833
+ tracking_page_ids.add(str(page_id))
834
+
835
+ if tracking_page_ids:
836
+ page_details = {"data": [], "source": "tracking_specs", "note": "Page IDs extracted from active ads - these are the most reliable for ad creation"}
837
+ for page_id in tracking_page_ids:
838
+ page_endpoint = f"{page_id}"
839
+ page_params = {
840
+ "fields": "id,name,username,category,fan_count,link,verification_status,picture"
841
+ }
842
+
843
+ page_data = await make_api_request(page_endpoint, access_token, page_params)
844
+ if "id" in page_data:
845
+ # Add additional context about this page ID being suitable for ads
846
+ page_data["_meta"] = {
847
+ "suitable_for_ads": True,
848
+ "found_in_tracking_specs": True,
849
+ "recommended_for_create_ad_creative": True
850
+ }
851
+ page_details["data"].append(page_data)
852
+ else:
853
+ page_details["data"].append({
854
+ "id": page_id,
855
+ "error": "Page details not found",
856
+ "_meta": {
857
+ "suitable_for_ads": True,
858
+ "found_in_tracking_specs": True,
859
+ "note": "Page ID exists in ads but details not accessible - you can still use this ID for ad creation"
860
+ }
861
+ })
862
+
863
+ if page_details["data"]:
864
+ return json.dumps(page_details, indent=2)
865
+
809
866
  # If all approaches failed, return empty data with a message
810
867
  return json.dumps({
811
868
  "data": [],
812
- "message": "No pages found associated with this account",
813
- "suggestion": "You may need to create a page or provide a page_id explicitly when creating ads"
869
+ "message": "No pages found associated with this account using automated methods",
870
+ "troubleshooting": {
871
+ "suggestion_1": "If you have existing ads, run 'get_ads' and look for page IDs in the 'tracking_specs' field",
872
+ "suggestion_2": "Use the exact page ID from existing ads' tracking_specs for creating new ad creatives",
873
+ "suggestion_3": "Verify your page ID format - it should be a numeric string (e.g., '123456789')",
874
+ "suggestion_4": "Check for digit transpositions or formatting errors in your page ID"
875
+ },
876
+ "note": "Based on community feedback, page IDs from existing ads' tracking_specs are the most reliable for ad creation"
814
877
  }, indent=2)
815
878
 
816
879
  except Exception as e:
@@ -1,6 +1,6 @@
1
1
  """Utility functions for Meta Ads API."""
2
2
 
3
- from typing import Optional, Dict, Any
3
+ from typing import Optional, Dict, Any, List
4
4
  import httpx
5
5
  import io
6
6
  from PIL import Image as PILImage
@@ -74,6 +74,64 @@ logger = setup_logging()
74
74
  # Global store for ad creative images
75
75
  ad_creative_images = {}
76
76
 
77
+
78
+ def extract_creative_image_urls(creative: Dict[str, Any]) -> List[str]:
79
+ """
80
+ Extract image URLs from a creative object for direct viewing.
81
+
82
+ Args:
83
+ creative: Meta Ads creative object
84
+
85
+ Returns:
86
+ List of image URLs found in the creative
87
+ """
88
+ image_urls = []
89
+
90
+ # Check for direct image_url field
91
+ if "image_url" in creative and creative["image_url"]:
92
+ image_urls.append(creative["image_url"])
93
+
94
+ # Check for thumbnail_url field
95
+ if "thumbnail_url" in creative and creative["thumbnail_url"]:
96
+ image_urls.append(creative["thumbnail_url"])
97
+
98
+ # Check object_story_spec for image URLs
99
+ if "object_story_spec" in creative:
100
+ story_spec = creative["object_story_spec"]
101
+
102
+ # Check link_data for image fields
103
+ if "link_data" in story_spec:
104
+ link_data = story_spec["link_data"]
105
+
106
+ # Check for picture field
107
+ if "picture" in link_data and link_data["picture"]:
108
+ image_urls.append(link_data["picture"])
109
+
110
+ # Check for image_url field in link_data
111
+ if "image_url" in link_data and link_data["image_url"]:
112
+ image_urls.append(link_data["image_url"])
113
+
114
+ # Check video_data for thumbnail (if present)
115
+ if "video_data" in story_spec and "image_url" in story_spec["video_data"]:
116
+ image_urls.append(story_spec["video_data"]["image_url"])
117
+
118
+ # Check asset_feed_spec for multiple images
119
+ if "asset_feed_spec" in creative and "images" in creative["asset_feed_spec"]:
120
+ for image in creative["asset_feed_spec"]["images"]:
121
+ if "url" in image and image["url"]:
122
+ image_urls.append(image["url"])
123
+
124
+ # Remove duplicates while preserving order
125
+ seen = set()
126
+ unique_urls = []
127
+ for url in image_urls:
128
+ if url not in seen:
129
+ seen.add(url)
130
+ unique_urls.append(url)
131
+
132
+ return unique_urls
133
+
134
+
77
135
  async def download_image(url: str) -> Optional[bytes]:
78
136
  """
79
137
  Download an image from a URL.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: meta-ads-mcp
3
- Version: 0.4.5
3
+ Version: 0.4.7
4
4
  Summary: Model Context Protocol (MCP) plugin for interacting with Meta Ads API
5
5
  Project-URL: Homepage, https://github.com/pipeboard-co/meta-ads-mcp
6
6
  Project-URL: Bug Tracker, https://github.com/pipeboard-co/meta-ads-mcp/issues
@@ -1,8 +1,8 @@
1
- meta_ads_mcp/__init__.py,sha256=AxnyJlgEJff8wT7T97juuWAkqoDQ9ekMbxMCCZx4Btk,1182
1
+ meta_ads_mcp/__init__.py,sha256=xGsc5RhOEUMCy8XODLn1QUM9Ixi1y9gSgLMCNPkV2oU,1182
2
2
  meta_ads_mcp/__main__.py,sha256=XaQt3iXftG_7f0Zu7Wop9SeFgrD2WBn0EQOaPMc27d8,207
3
3
  meta_ads_mcp/core/__init__.py,sha256=XVJjMOfdgnqxy3k8vCn2PCf7za8fMk4BdgJGiSFCVZY,1209
4
4
  meta_ads_mcp/core/accounts.py,sha256=Nmp7lPxO9wmq25jWV7_H0LIqnEbBhpCVBlLGW2HUaq0,2277
5
- meta_ads_mcp/core/ads.py,sha256=Ss0VaSjm7J4g7zUZLPLzz-WGC8h-SdhV_mtBRUNpkgE,29808
5
+ meta_ads_mcp/core/ads.py,sha256=t3COpPA-TGsOYndq0vWA9XR0L7sg1cGHNrM2HK24Q6I,33424
6
6
  meta_ads_mcp/core/ads_library.py,sha256=onStn9UkRqYDC60gOPS-iKDtP1plz6DygUb7hUZ0Jw8,2807
7
7
  meta_ads_mcp/core/adsets.py,sha256=k76rm3rkhEebUzvBnM_QaVktrzGTKvTJOtWbBd6s3i8,10399
8
8
  meta_ads_mcp/core/api.py,sha256=aAzM6Q75VQOFXtr5D-mDmBRhxWK4wsiODsJYnR3mpDI,14994
@@ -18,9 +18,9 @@ meta_ads_mcp/core/pipeboard_auth.py,sha256=VvbxEB8ZOhnMccLU7HI1HgaPWHCl5NGrzZCm-
18
18
  meta_ads_mcp/core/reports.py,sha256=Dv3hfsPOR7IZ9WrYrKd_6SNgZl-USIphg7knva3UYAw,5747
19
19
  meta_ads_mcp/core/resources.py,sha256=-zIIfZulpo76vcKv6jhAlQq91cR2SZ3cjYZt3ek3x0w,1236
20
20
  meta_ads_mcp/core/server.py,sha256=mmhtcyB7h1aO6jK4njLztPdAebPDmc3mhA7DksR1nlY,17583
21
- meta_ads_mcp/core/utils.py,sha256=DsizDYuJnWUpkbShV1y5Qe8t47Qf59aPZ6O9v0hzdkY,6705
22
- meta_ads_mcp-0.4.5.dist-info/METADATA,sha256=jS17u-iv9a7XlbmsIqZT9-FPWfUsmEpfPY7U68BCWJA,17580
23
- meta_ads_mcp-0.4.5.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
24
- meta_ads_mcp-0.4.5.dist-info/entry_points.txt,sha256=Dv2RkoBjRJBqj6CyhwqGIiwPCD-SCL1-7B9-zmVRuv0,57
25
- meta_ads_mcp-0.4.5.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
26
- meta_ads_mcp-0.4.5.dist-info/RECORD,,
21
+ meta_ads_mcp/core/utils.py,sha256=ofKUhyo-5SZoJVuBeTVFPPQCffk0UKpwmDMrd8qQxNc,8715
22
+ meta_ads_mcp-0.4.7.dist-info/METADATA,sha256=uxNO8COPSkDIPtTrw-fNDoty0k8IbFCZtVS2QfyO2lA,17580
23
+ meta_ads_mcp-0.4.7.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
24
+ meta_ads_mcp-0.4.7.dist-info/entry_points.txt,sha256=Dv2RkoBjRJBqj6CyhwqGIiwPCD-SCL1-7B9-zmVRuv0,57
25
+ meta_ads_mcp-0.4.7.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
26
+ meta_ads_mcp-0.4.7.dist-info/RECORD,,