meta-ads-mcp 0.4.6__py3-none-any.whl → 0.4.8__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- meta_ads_mcp/__init__.py +1 -1
- meta_ads_mcp/core/ads.py +26 -11
- meta_ads_mcp/core/utils.py +59 -1
- {meta_ads_mcp-0.4.6.dist-info → meta_ads_mcp-0.4.8.dist-info}/METADATA +1 -1
- {meta_ads_mcp-0.4.6.dist-info → meta_ads_mcp-0.4.8.dist-info}/RECORD +8 -8
- {meta_ads_mcp-0.4.6.dist-info → meta_ads_mcp-0.4.8.dist-info}/WHEEL +0 -0
- {meta_ads_mcp-0.4.6.dist-info → meta_ads_mcp-0.4.8.dist-info}/entry_points.txt +0 -0
- {meta_ads_mcp-0.4.6.dist-info → meta_ads_mcp-0.4.8.dist-info}/licenses/LICENSE +0 -0
meta_ads_mcp/__init__.py
CHANGED
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'] =
|
|
182
|
+
creative['image_urls_for_viewing'] = extract_creative_image_urls(creative)
|
|
183
183
|
|
|
184
184
|
return json.dumps(data, indent=2)
|
|
185
185
|
|
|
@@ -251,14 +251,27 @@ async def get_ad_image(access_token: str = None, ad_id: str = None) -> Image:
|
|
|
251
251
|
if not image_hashes:
|
|
252
252
|
# If no hashes found, try to extract from the first creative we found in the API
|
|
253
253
|
# Get creative for ad to try to extract hash
|
|
254
|
-
creative_json = await get_ad_creatives(
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
254
|
+
creative_json = await get_ad_creatives(access_token=access_token, ad_id=ad_id)
|
|
255
|
+
# The result is wrapped by @meta_api_tool, so we need to extract the data
|
|
256
|
+
creative_wrapper = json.loads(creative_json)
|
|
257
|
+
creative_data = json.loads(creative_wrapper["data"])
|
|
258
|
+
|
|
259
|
+
# Try to extract hash from data array
|
|
260
|
+
if "data" in creative_data and creative_data["data"]:
|
|
261
|
+
for creative in creative_data["data"]:
|
|
262
|
+
# Check object_story_spec for image hash
|
|
263
|
+
if "object_story_spec" in creative and "link_data" in creative["object_story_spec"]:
|
|
264
|
+
link_data = creative["object_story_spec"]["link_data"]
|
|
265
|
+
if "image_hash" in link_data:
|
|
266
|
+
image_hashes.append(link_data["image_hash"])
|
|
267
|
+
# Check direct image_hash on creative
|
|
268
|
+
elif "image_hash" in creative:
|
|
269
|
+
image_hashes.append(creative["image_hash"])
|
|
270
|
+
# Check asset_feed_spec for image hashes
|
|
271
|
+
elif "asset_feed_spec" in creative and "images" in creative["asset_feed_spec"]:
|
|
272
|
+
images = creative["asset_feed_spec"]["images"]
|
|
273
|
+
if images and len(images) > 0 and "hash" in images[0]:
|
|
274
|
+
image_hashes.append(images[0]["hash"])
|
|
262
275
|
|
|
263
276
|
if not image_hashes:
|
|
264
277
|
return "Error: No image hashes found in creative"
|
|
@@ -380,7 +393,9 @@ async def save_ad_image_locally(access_token: str = None, ad_id: str = None, out
|
|
|
380
393
|
if not image_hashes:
|
|
381
394
|
# Fallback attempt (as in get_ad_image)
|
|
382
395
|
creative_json = await get_ad_creatives(ad_id=ad_id, access_token=access_token) # Ensure ad_id is passed correctly
|
|
383
|
-
|
|
396
|
+
# The result is wrapped by @meta_api_tool, so we need to extract the data
|
|
397
|
+
creative_wrapper = json.loads(creative_json)
|
|
398
|
+
creative_data_list = json.loads(creative_wrapper["data"])
|
|
384
399
|
if 'data' in creative_data_list and creative_data_list['data']:
|
|
385
400
|
first_creative = creative_data_list['data'][0]
|
|
386
401
|
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']:
|
meta_ads_mcp/core/utils.py
CHANGED
|
@@ -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.
|
|
3
|
+
Version: 0.4.8
|
|
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=
|
|
1
|
+
meta_ads_mcp/__init__.py,sha256=gDY8drq5dj3hFy9EmqLzzto7l_nD8pXFULiiEwZL8YU,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=
|
|
5
|
+
meta_ads_mcp/core/ads.py,sha256=wKQBrDSlZLUZDUEIcuphf3ltBbuYM5vg5mZ31UiZ9mE,34432
|
|
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=
|
|
22
|
-
meta_ads_mcp-0.4.
|
|
23
|
-
meta_ads_mcp-0.4.
|
|
24
|
-
meta_ads_mcp-0.4.
|
|
25
|
-
meta_ads_mcp-0.4.
|
|
26
|
-
meta_ads_mcp-0.4.
|
|
21
|
+
meta_ads_mcp/core/utils.py,sha256=ofKUhyo-5SZoJVuBeTVFPPQCffk0UKpwmDMrd8qQxNc,8715
|
|
22
|
+
meta_ads_mcp-0.4.8.dist-info/METADATA,sha256=TYL2W6RFXPEI3yLw71SmqmuJq2M5i2fxvzrHm-N2URU,17580
|
|
23
|
+
meta_ads_mcp-0.4.8.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
24
|
+
meta_ads_mcp-0.4.8.dist-info/entry_points.txt,sha256=Dv2RkoBjRJBqj6CyhwqGIiwPCD-SCL1-7B9-zmVRuv0,57
|
|
25
|
+
meta_ads_mcp-0.4.8.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
26
|
+
meta_ads_mcp-0.4.8.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|