meta-ads-mcp 0.7.9__py3-none-any.whl → 0.7.10__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 +201 -16
- {meta_ads_mcp-0.7.9.dist-info → meta_ads_mcp-0.7.10.dist-info}/METADATA +1 -1
- {meta_ads_mcp-0.7.9.dist-info → meta_ads_mcp-0.7.10.dist-info}/RECORD +7 -7
- {meta_ads_mcp-0.7.9.dist-info → meta_ads_mcp-0.7.10.dist-info}/WHEEL +0 -0
- {meta_ads_mcp-0.7.9.dist-info → meta_ads_mcp-0.7.10.dist-info}/entry_points.txt +0 -0
- {meta_ads_mcp-0.7.9.dist-info → meta_ads_mcp-0.7.10.dist-info}/licenses/LICENSE +0 -0
meta_ads_mcp/__init__.py
CHANGED
meta_ads_mcp/core/ads.py
CHANGED
|
@@ -670,31 +670,31 @@ async def create_ad_creative(
|
|
|
670
670
|
if not account_id.startswith("act_"):
|
|
671
671
|
account_id = f"act_{account_id}"
|
|
672
672
|
|
|
673
|
-
# If no page ID is provided,
|
|
673
|
+
# Enhanced page discovery: If no page ID is provided, use robust discovery methods
|
|
674
674
|
if not page_id:
|
|
675
675
|
try:
|
|
676
|
-
#
|
|
677
|
-
|
|
678
|
-
pages_params = {
|
|
679
|
-
"fields": "id,name",
|
|
680
|
-
"limit": 1
|
|
681
|
-
}
|
|
682
|
-
|
|
683
|
-
pages_data = await make_api_request(pages_endpoint, access_token, pages_params)
|
|
676
|
+
# Use the comprehensive page discovery logic from get_account_pages
|
|
677
|
+
page_discovery_result = await _discover_pages_for_account(account_id, access_token)
|
|
684
678
|
|
|
685
|
-
if "
|
|
686
|
-
page_id =
|
|
687
|
-
|
|
679
|
+
if page_discovery_result.get("success"):
|
|
680
|
+
page_id = page_discovery_result["page_id"]
|
|
681
|
+
page_name = page_discovery_result.get("page_name", "Unknown")
|
|
682
|
+
print(f"Auto-discovered page ID: {page_id} ({page_name})")
|
|
688
683
|
else:
|
|
689
684
|
return json.dumps({
|
|
690
|
-
"error": "No page ID provided and no pages found for this account",
|
|
691
|
-
"
|
|
685
|
+
"error": "No page ID provided and no suitable pages found for this account",
|
|
686
|
+
"details": page_discovery_result.get("message", "Page discovery failed"),
|
|
687
|
+
"suggestions": [
|
|
688
|
+
"Use get_account_pages to see available pages",
|
|
689
|
+
"Use search_pages_by_name to find specific pages",
|
|
690
|
+
"Provide a page_id parameter manually"
|
|
691
|
+
]
|
|
692
692
|
}, indent=2)
|
|
693
693
|
except Exception as e:
|
|
694
694
|
return json.dumps({
|
|
695
|
-
"error": "Error
|
|
695
|
+
"error": "Error during page discovery",
|
|
696
696
|
"details": str(e),
|
|
697
|
-
"suggestion": "Please provide a page_id parameter"
|
|
697
|
+
"suggestion": "Please provide a page_id parameter or use get_account_pages to find available pages"
|
|
698
698
|
}, indent=2)
|
|
699
699
|
|
|
700
700
|
# Prepare the creative data
|
|
@@ -759,6 +759,191 @@ async def create_ad_creative(
|
|
|
759
759
|
}, indent=2)
|
|
760
760
|
|
|
761
761
|
|
|
762
|
+
async def _discover_pages_for_account(account_id: str, access_token: str) -> dict:
|
|
763
|
+
"""
|
|
764
|
+
Internal function to discover pages for an account using multiple approaches.
|
|
765
|
+
Returns the best available page ID for ad creation.
|
|
766
|
+
"""
|
|
767
|
+
try:
|
|
768
|
+
# Approach 1: Extract page IDs from tracking_specs in ads (most reliable)
|
|
769
|
+
endpoint = f"{account_id}/ads"
|
|
770
|
+
params = {
|
|
771
|
+
"fields": "id,name,adset_id,campaign_id,status,creative,created_time,updated_time,bid_amount,conversion_domain,tracking_specs",
|
|
772
|
+
"limit": 100
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
tracking_ads_data = await make_api_request(endpoint, access_token, params)
|
|
776
|
+
|
|
777
|
+
tracking_page_ids = set()
|
|
778
|
+
if "data" in tracking_ads_data:
|
|
779
|
+
for ad in tracking_ads_data.get("data", []):
|
|
780
|
+
tracking_specs = ad.get("tracking_specs", [])
|
|
781
|
+
if isinstance(tracking_specs, list):
|
|
782
|
+
for spec in tracking_specs:
|
|
783
|
+
if isinstance(spec, dict) and "page" in spec:
|
|
784
|
+
page_list = spec["page"]
|
|
785
|
+
if isinstance(page_list, list):
|
|
786
|
+
for page_id in page_list:
|
|
787
|
+
if isinstance(page_id, (str, int)) and str(page_id).isdigit():
|
|
788
|
+
tracking_page_ids.add(str(page_id))
|
|
789
|
+
|
|
790
|
+
if tracking_page_ids:
|
|
791
|
+
# Get details for the first page found
|
|
792
|
+
page_id = list(tracking_page_ids)[0]
|
|
793
|
+
page_endpoint = f"{page_id}"
|
|
794
|
+
page_params = {
|
|
795
|
+
"fields": "id,name,username,category,fan_count,link,verification_status,picture"
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
page_data = await make_api_request(page_endpoint, access_token, page_params)
|
|
799
|
+
if "id" in page_data:
|
|
800
|
+
return {
|
|
801
|
+
"success": True,
|
|
802
|
+
"page_id": page_id,
|
|
803
|
+
"page_name": page_data.get("name", "Unknown"),
|
|
804
|
+
"source": "tracking_specs",
|
|
805
|
+
"note": "Page ID extracted from existing ads - most reliable for ad creation"
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
# Approach 2: Try client_pages endpoint
|
|
809
|
+
endpoint = f"{account_id}/client_pages"
|
|
810
|
+
params = {
|
|
811
|
+
"fields": "id,name,username,category,fan_count,link,verification_status,picture"
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
client_pages_data = await make_api_request(endpoint, access_token, params)
|
|
815
|
+
|
|
816
|
+
if "data" in client_pages_data and client_pages_data["data"]:
|
|
817
|
+
page = client_pages_data["data"][0]
|
|
818
|
+
return {
|
|
819
|
+
"success": True,
|
|
820
|
+
"page_id": page["id"],
|
|
821
|
+
"page_name": page.get("name", "Unknown"),
|
|
822
|
+
"source": "client_pages"
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
# Approach 3: Try assigned_pages endpoint
|
|
826
|
+
pages_endpoint = f"{account_id}/assigned_pages"
|
|
827
|
+
pages_params = {
|
|
828
|
+
"fields": "id,name",
|
|
829
|
+
"limit": 1
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
pages_data = await make_api_request(pages_endpoint, access_token, pages_params)
|
|
833
|
+
|
|
834
|
+
if "data" in pages_data and pages_data["data"]:
|
|
835
|
+
page = pages_data["data"][0]
|
|
836
|
+
return {
|
|
837
|
+
"success": True,
|
|
838
|
+
"page_id": page["id"],
|
|
839
|
+
"page_name": page.get("name", "Unknown"),
|
|
840
|
+
"source": "assigned_pages"
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
# If all approaches failed
|
|
844
|
+
return {
|
|
845
|
+
"success": False,
|
|
846
|
+
"message": "No suitable pages found for this account",
|
|
847
|
+
"note": "Try using get_account_pages to see all available pages or provide page_id manually"
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
except Exception as e:
|
|
851
|
+
return {
|
|
852
|
+
"success": False,
|
|
853
|
+
"message": f"Error during page discovery: {str(e)}"
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
|
|
857
|
+
async def _search_pages_by_name_core(access_token: str, account_id: str, search_term: str = None) -> str:
|
|
858
|
+
"""
|
|
859
|
+
Core logic for searching pages by name.
|
|
860
|
+
|
|
861
|
+
Args:
|
|
862
|
+
access_token: Meta API access token
|
|
863
|
+
account_id: Meta Ads account ID (format: act_XXXXXXXXX)
|
|
864
|
+
search_term: Search term to find pages by name (optional - returns all pages if not provided)
|
|
865
|
+
|
|
866
|
+
Returns:
|
|
867
|
+
JSON string with search results
|
|
868
|
+
"""
|
|
869
|
+
# Ensure account_id has the 'act_' prefix
|
|
870
|
+
if not account_id.startswith("act_"):
|
|
871
|
+
account_id = f"act_{account_id}"
|
|
872
|
+
|
|
873
|
+
try:
|
|
874
|
+
# Use the internal discovery function directly
|
|
875
|
+
page_discovery_result = await _discover_pages_for_account(account_id, access_token)
|
|
876
|
+
|
|
877
|
+
if not page_discovery_result.get("success"):
|
|
878
|
+
return json.dumps({
|
|
879
|
+
"data": [],
|
|
880
|
+
"message": "No pages found for this account",
|
|
881
|
+
"details": page_discovery_result.get("message", "Page discovery failed")
|
|
882
|
+
}, indent=2)
|
|
883
|
+
|
|
884
|
+
# Create a single page result
|
|
885
|
+
page_data = {
|
|
886
|
+
"id": page_discovery_result["page_id"],
|
|
887
|
+
"name": page_discovery_result.get("page_name", "Unknown"),
|
|
888
|
+
"source": page_discovery_result.get("source", "unknown")
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
all_pages_data = {"data": [page_data]}
|
|
892
|
+
|
|
893
|
+
# Filter pages by search term if provided
|
|
894
|
+
if search_term:
|
|
895
|
+
search_term_lower = search_term.lower()
|
|
896
|
+
filtered_pages = []
|
|
897
|
+
|
|
898
|
+
for page in all_pages_data["data"]:
|
|
899
|
+
page_name = page.get("name", "").lower()
|
|
900
|
+
if search_term_lower in page_name:
|
|
901
|
+
filtered_pages.append(page)
|
|
902
|
+
|
|
903
|
+
return json.dumps({
|
|
904
|
+
"data": filtered_pages,
|
|
905
|
+
"search_term": search_term,
|
|
906
|
+
"total_found": len(filtered_pages),
|
|
907
|
+
"total_available": len(all_pages_data["data"])
|
|
908
|
+
}, indent=2)
|
|
909
|
+
else:
|
|
910
|
+
# Return all pages if no search term provided
|
|
911
|
+
return json.dumps({
|
|
912
|
+
"data": all_pages_data["data"],
|
|
913
|
+
"total_available": len(all_pages_data["data"]),
|
|
914
|
+
"note": "Use search_term parameter to filter pages by name"
|
|
915
|
+
}, indent=2)
|
|
916
|
+
|
|
917
|
+
except Exception as e:
|
|
918
|
+
return json.dumps({
|
|
919
|
+
"error": "Failed to search pages by name",
|
|
920
|
+
"details": str(e)
|
|
921
|
+
}, indent=2)
|
|
922
|
+
|
|
923
|
+
|
|
924
|
+
@mcp_server.tool()
|
|
925
|
+
@meta_api_tool
|
|
926
|
+
async def search_pages_by_name(access_token: str = None, account_id: str = None, search_term: str = None) -> str:
|
|
927
|
+
"""
|
|
928
|
+
Search for pages by name within an account.
|
|
929
|
+
|
|
930
|
+
Args:
|
|
931
|
+
access_token: Meta API access token (optional - will use cached token if not provided)
|
|
932
|
+
account_id: Meta Ads account ID (format: act_XXXXXXXXX)
|
|
933
|
+
search_term: Search term to find pages by name (optional - returns all pages if not provided)
|
|
934
|
+
|
|
935
|
+
Returns:
|
|
936
|
+
JSON response with matching pages
|
|
937
|
+
"""
|
|
938
|
+
# Check required parameters
|
|
939
|
+
if not account_id:
|
|
940
|
+
return json.dumps({"error": "No account ID provided"}, indent=2)
|
|
941
|
+
|
|
942
|
+
# Call the core function
|
|
943
|
+
result = await _search_pages_by_name_core(access_token, account_id, search_term)
|
|
944
|
+
return result
|
|
945
|
+
|
|
946
|
+
|
|
762
947
|
@mcp_server.tool()
|
|
763
948
|
@meta_api_tool
|
|
764
949
|
async def get_account_pages(access_token: str = None, account_id: str = None) -> str:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: meta-ads-mcp
|
|
3
|
-
Version: 0.7.
|
|
3
|
+
Version: 0.7.10
|
|
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=Y0Oj0zMOmh2TX_E4iBNehH9NFlM1w9O3injKT8FJMbw,1493
|
|
2
2
|
meta_ads_mcp/__main__.py,sha256=XaQt3iXftG_7f0Zu7Wop9SeFgrD2WBn0EQOaPMc27d8,207
|
|
3
3
|
meta_ads_mcp/core/__init__.py,sha256=6nYdue6yRepkt6JTAoPGhGbS51qfDSvmczRrDwYOG6A,1709
|
|
4
4
|
meta_ads_mcp/core/accounts.py,sha256=4IAdGLZ4WE4j4pGW6E0qaXcXqbUIW6Wk2kuQUtlmRTQ,4030
|
|
5
|
-
meta_ads_mcp/core/ads.py,sha256=
|
|
5
|
+
meta_ads_mcp/core/ads.py,sha256=lUq5te8SDFHS2gn8IlwVa0svyFyqJBMvWtuxy1hacFI,44428
|
|
6
6
|
meta_ads_mcp/core/ads_library.py,sha256=BBGVbtjO5eFV42iiY3XPU-wIV8HupzUKpHgPBrydSvU,3232
|
|
7
7
|
meta_ads_mcp/core/adsets.py,sha256=vY5JNHmGK1a_sQ5B1LnjxLYXzs5_jOajTTjWHRDJ4_Y,12518
|
|
8
8
|
meta_ads_mcp/core/api.py,sha256=aAzM6Q75VQOFXtr5D-mDmBRhxWK4wsiODsJYnR3mpDI,14994
|
|
@@ -21,8 +21,8 @@ meta_ads_mcp/core/resources.py,sha256=-zIIfZulpo76vcKv6jhAlQq91cR2SZ3cjYZt3ek3x0
|
|
|
21
21
|
meta_ads_mcp/core/server.py,sha256=WhbAag7xdhbGcp7rnU4sKhqXJ8Slapa_ba3T23Yp_2U,17889
|
|
22
22
|
meta_ads_mcp/core/targeting.py,sha256=3HW1qirEdwaQurlBZGenbIwawcb5J06ghJKRfgu9ZEs,6318
|
|
23
23
|
meta_ads_mcp/core/utils.py,sha256=ytj41yC5SqduLrAiZYBSd6OUwlJRaIClTwnnYKpNFds,9387
|
|
24
|
-
meta_ads_mcp-0.7.
|
|
25
|
-
meta_ads_mcp-0.7.
|
|
26
|
-
meta_ads_mcp-0.7.
|
|
27
|
-
meta_ads_mcp-0.7.
|
|
28
|
-
meta_ads_mcp-0.7.
|
|
24
|
+
meta_ads_mcp-0.7.10.dist-info/METADATA,sha256=DCV6TdsYdyLogsgvOpIu8hBjawrGo1FWiF4PuKp20AU,20410
|
|
25
|
+
meta_ads_mcp-0.7.10.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
26
|
+
meta_ads_mcp-0.7.10.dist-info/entry_points.txt,sha256=Dv2RkoBjRJBqj6CyhwqGIiwPCD-SCL1-7B9-zmVRuv0,57
|
|
27
|
+
meta_ads_mcp-0.7.10.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
28
|
+
meta_ads_mcp-0.7.10.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|