meta-ads-mcp 1.0.12__py3-none-any.whl → 1.0.15__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
@@ -6,7 +6,7 @@ This package provides a Meta Ads MCP integration
6
6
 
7
7
  from meta_ads_mcp.core.server import main
8
8
 
9
- __version__ = "1.0.12"
9
+ __version__ = "1.0.15"
10
10
 
11
11
  __all__ = [
12
12
  'get_ad_accounts',
meta_ads_mcp/core/ads.py CHANGED
@@ -831,32 +831,18 @@ async def create_ad_creative(
831
831
  if description and descriptions:
832
832
  return json.dumps({"error": "Cannot specify both 'description' and 'descriptions'. Use 'description' for single description or 'descriptions' for multiple."}, indent=2)
833
833
 
834
- # Convert simple parameters to complex format for internal processing
835
- final_headlines = None
836
- final_descriptions = None
837
-
838
- if headline:
839
- final_headlines = [headline]
840
- elif headlines:
841
- final_headlines = headlines
842
-
843
- if description:
844
- final_descriptions = [description]
845
- elif descriptions:
846
- final_descriptions = descriptions
847
-
848
- # Validate dynamic creative parameters
849
- if final_headlines:
850
- if len(final_headlines) > 5:
834
+ # Validate dynamic creative parameters (plural forms only)
835
+ if headlines:
836
+ if len(headlines) > 5:
851
837
  return json.dumps({"error": "Maximum 5 headlines allowed for dynamic creatives"}, indent=2)
852
- for i, h in enumerate(final_headlines):
838
+ for i, h in enumerate(headlines):
853
839
  if len(h) > 40:
854
840
  return json.dumps({"error": f"Headline {i+1} exceeds 40 character limit"}, indent=2)
855
841
 
856
- if final_descriptions:
857
- if len(final_descriptions) > 5:
842
+ if descriptions:
843
+ if len(descriptions) > 5:
858
844
  return json.dumps({"error": "Maximum 5 descriptions allowed for dynamic creatives"}, indent=2)
859
- for i, d in enumerate(final_descriptions):
845
+ for i, d in enumerate(descriptions):
860
846
  if len(d) > 125:
861
847
  return json.dumps({"error": f"Description {i+1} exceeds 125 character limit"}, indent=2)
862
848
 
@@ -866,8 +852,9 @@ async def create_ad_creative(
866
852
  }
867
853
 
868
854
  # Choose between asset_feed_spec (dynamic creative) or object_story_spec (traditional)
869
- if final_headlines or final_descriptions:
870
- # Use asset_feed_spec for dynamic creatives
855
+ # ONLY use asset_feed_spec when user explicitly provides plural parameters (headlines/descriptions)
856
+ if headlines or descriptions:
857
+ # Use asset_feed_spec for dynamic creatives with multiple variants
871
858
  asset_feed_spec = {
872
859
  "ad_formats": ["SINGLE_IMAGE"],
873
860
  "images": [{"hash": image_hash}],
@@ -875,12 +862,12 @@ async def create_ad_creative(
875
862
  }
876
863
 
877
864
  # Handle headlines
878
- if final_headlines:
879
- asset_feed_spec["headlines"] = [{"text": headline_text} for headline_text in final_headlines]
865
+ if headlines:
866
+ asset_feed_spec["headlines"] = [{"text": headline_text} for headline_text in headlines]
880
867
 
881
868
  # Handle descriptions
882
- if final_descriptions:
883
- asset_feed_spec["descriptions"] = [{"text": description_text} for description_text in final_descriptions]
869
+ if descriptions:
870
+ asset_feed_spec["descriptions"] = [{"text": description_text} for description_text in descriptions]
884
871
 
885
872
  # Add message as primary_texts if provided
886
873
  if message:
@@ -897,7 +884,7 @@ async def create_ad_creative(
897
884
  "page_id": page_id
898
885
  }
899
886
  else:
900
- # Use traditional object_story_spec for single creative
887
+ # Use traditional object_story_spec with link_data for simple creatives
901
888
  creative_data["object_story_spec"] = {
902
889
  "page_id": page_id,
903
890
  "link_data": {
@@ -909,17 +896,25 @@ async def create_ad_creative(
909
896
  # Add optional parameters if provided
910
897
  if message:
911
898
  creative_data["object_story_spec"]["link_data"]["message"] = message
899
+
900
+ # Add headline (singular) to link_data
901
+ if headline:
902
+ creative_data["object_story_spec"]["link_data"]["name"] = headline
903
+
904
+ # Add description (singular) to link_data
905
+ if description:
906
+ creative_data["object_story_spec"]["link_data"]["description"] = description
907
+
908
+ # Add call_to_action to link_data for simple creatives
909
+ if call_to_action_type:
910
+ creative_data["object_story_spec"]["link_data"]["call_to_action"] = {
911
+ "type": call_to_action_type
912
+ }
912
913
 
913
914
  # Add dynamic creative spec if provided
914
915
  if dynamic_creative_spec:
915
916
  creative_data["dynamic_creative_spec"] = dynamic_creative_spec
916
917
 
917
- # Only add call_to_action to object_story_spec if we're not using asset_feed_spec
918
- if call_to_action_type and "asset_feed_spec" not in creative_data:
919
- creative_data["object_story_spec"]["link_data"]["call_to_action"] = {
920
- "type": call_to_action_type
921
- }
922
-
923
918
  if instagram_actor_id:
924
919
  creative_data["instagram_actor_id"] = instagram_actor_id
925
920
 
@@ -998,32 +993,18 @@ async def update_ad_creative(
998
993
  if description and descriptions:
999
994
  return json.dumps({"error": "Cannot specify both 'description' and 'descriptions'. Use 'description' for single description or 'descriptions' for multiple."}, indent=2)
1000
995
 
1001
- # Convert simple parameters to complex format for internal processing
1002
- final_headlines = None
1003
- final_descriptions = None
1004
-
1005
- if headline:
1006
- final_headlines = [headline]
1007
- elif headlines:
1008
- final_headlines = headlines
1009
-
1010
- if description:
1011
- final_descriptions = [description]
1012
- elif descriptions:
1013
- final_descriptions = descriptions
1014
-
1015
- # Validate dynamic creative parameters
1016
- if final_headlines:
1017
- if len(final_headlines) > 5:
996
+ # Validate dynamic creative parameters (plural forms only)
997
+ if headlines:
998
+ if len(headlines) > 5:
1018
999
  return json.dumps({"error": "Maximum 5 headlines allowed for dynamic creatives"}, indent=2)
1019
- for i, h in enumerate(final_headlines):
1000
+ for i, h in enumerate(headlines):
1020
1001
  if len(h) > 40:
1021
1002
  return json.dumps({"error": f"Headline {i+1} exceeds 40 character limit"}, indent=2)
1022
1003
 
1023
- if final_descriptions:
1024
- if len(final_descriptions) > 5:
1004
+ if descriptions:
1005
+ if len(descriptions) > 5:
1025
1006
  return json.dumps({"error": "Maximum 5 descriptions allowed for dynamic creatives"}, indent=2)
1026
- for i, d in enumerate(final_descriptions):
1007
+ for i, d in enumerate(descriptions):
1027
1008
  if len(d) > 125:
1028
1009
  return json.dumps({"error": f"Description {i+1} exceeds 125 character limit"}, indent=2)
1029
1010
 
@@ -1033,45 +1014,58 @@ async def update_ad_creative(
1033
1014
  if name:
1034
1015
  update_data["name"] = name
1035
1016
 
1036
- if message:
1037
- update_data["object_story_spec"] = {"link_data": {"message": message}}
1038
-
1039
- # Handle dynamic creative assets via asset_feed_spec
1040
- if final_headlines or final_descriptions or dynamic_creative_spec:
1017
+ # Choose between asset_feed_spec (dynamic creative) or object_story_spec (traditional)
1018
+ # ONLY use asset_feed_spec when user explicitly provides plural parameters (headlines/descriptions)
1019
+ if headlines or descriptions or dynamic_creative_spec:
1020
+ # Handle dynamic creative assets via asset_feed_spec
1041
1021
  asset_feed_spec = {}
1042
1022
 
1043
1023
  # Add required ad_formats field for dynamic creatives
1044
1024
  asset_feed_spec["ad_formats"] = ["SINGLE_IMAGE"]
1045
1025
 
1046
1026
  # Handle headlines
1047
- if final_headlines:
1048
- asset_feed_spec["headlines"] = [{"text": headline_text} for headline_text in final_headlines]
1027
+ if headlines:
1028
+ asset_feed_spec["headlines"] = [{"text": headline_text} for headline_text in headlines]
1049
1029
 
1050
1030
  # Handle descriptions
1051
- if final_descriptions:
1052
- asset_feed_spec["descriptions"] = [{"text": description_text} for description_text in final_descriptions]
1031
+ if descriptions:
1032
+ asset_feed_spec["descriptions"] = [{"text": description_text} for description_text in descriptions]
1053
1033
 
1054
1034
  # Add message as primary_texts if provided
1055
1035
  if message:
1056
1036
  asset_feed_spec["primary_texts"] = [{"text": message}]
1057
1037
 
1038
+ # Add call_to_action_types if provided
1039
+ if call_to_action_type:
1040
+ asset_feed_spec["call_to_action_types"] = [call_to_action_type]
1041
+
1058
1042
  update_data["asset_feed_spec"] = asset_feed_spec
1043
+ else:
1044
+ # Use traditional object_story_spec with link_data for simple creatives
1045
+ if message or headline or description or call_to_action_type:
1046
+ update_data["object_story_spec"] = {"link_data": {}}
1047
+
1048
+ if message:
1049
+ update_data["object_story_spec"]["link_data"]["message"] = message
1050
+
1051
+ # Add headline (singular) to link_data
1052
+ if headline:
1053
+ update_data["object_story_spec"]["link_data"]["name"] = headline
1054
+
1055
+ # Add description (singular) to link_data
1056
+ if description:
1057
+ update_data["object_story_spec"]["link_data"]["description"] = description
1058
+
1059
+ # Add call_to_action to link_data for simple creatives
1060
+ if call_to_action_type:
1061
+ update_data["object_story_spec"]["link_data"]["call_to_action"] = {
1062
+ "type": call_to_action_type
1063
+ }
1059
1064
 
1060
1065
  # Add dynamic creative spec if provided
1061
1066
  if dynamic_creative_spec:
1062
1067
  update_data["dynamic_creative_spec"] = dynamic_creative_spec
1063
1068
 
1064
- # Handle call_to_action - add to asset_feed_spec if using dynamic creative, otherwise to object_story_spec
1065
- if call_to_action_type:
1066
- if "asset_feed_spec" in update_data:
1067
- update_data["asset_feed_spec"]["call_to_action_types"] = [call_to_action_type]
1068
- else:
1069
- if "object_story_spec" not in update_data:
1070
- update_data["object_story_spec"] = {"link_data": {}}
1071
- update_data["object_story_spec"]["link_data"]["call_to_action"] = {
1072
- "type": call_to_action_type
1073
- }
1074
-
1075
1069
  # Prepare the API endpoint for updating the creative
1076
1070
  endpoint = f"{creative_id}"
1077
1071
 
meta_ads_mcp/core/api.py CHANGED
@@ -6,7 +6,8 @@ import httpx
6
6
  import asyncio
7
7
  import functools
8
8
  import os
9
- from .auth import needs_authentication, get_current_access_token, auth_manager, start_callback_server, shutdown_callback_server
9
+ from . import auth
10
+ from .auth import needs_authentication, auth_manager, start_callback_server, shutdown_callback_server
10
11
  from .utils import logger
11
12
 
12
13
  # Constants
@@ -203,7 +204,7 @@ def meta_api_tool(func):
203
204
  # If access_token is not in kwargs or not kwargs['access_token'], try to get it from auth_manager
204
205
  if 'access_token' not in kwargs or not kwargs['access_token']:
205
206
  try:
206
- access_token = await get_current_access_token()
207
+ access_token = await auth.get_current_access_token()
207
208
  if access_token:
208
209
  kwargs['access_token'] = access_token
209
210
  logger.debug("Using access token from auth_manager")
@@ -29,7 +29,8 @@ from typing import Optional
29
29
  import asyncio
30
30
  import os
31
31
  from .api import meta_api_tool
32
- from .auth import start_callback_server, shutdown_callback_server, auth_manager, get_current_access_token
32
+ from . import auth
33
+ from .auth import start_callback_server, shutdown_callback_server, auth_manager
33
34
  from .server import mcp_server
34
35
  from .utils import logger, META_APP_SECRET
35
36
  from .pipeboard_auth import pipeboard_auth_manager
@@ -6,7 +6,7 @@ import httpx
6
6
  from typing import Optional, Dict, Any, List, Union
7
7
  from .server import mcp_server
8
8
  from .api import meta_api_tool
9
- from .auth import get_current_access_token
9
+ from . import auth
10
10
  from .http_auth_integration import FastMCPAuthIntegration
11
11
 
12
12
 
@@ -204,7 +204,7 @@ async def _forward_duplication_request(resource_type: str, resource_id: str, acc
204
204
 
205
205
  # Use provided access_token parameter if no Facebook token found in context
206
206
  if not facebook_token:
207
- facebook_token = access_token if access_token else await get_current_access_token()
207
+ facebook_token = access_token if access_token else await auth.get_current_access_token()
208
208
 
209
209
  # Validate we have both required tokens
210
210
  if not pipeboard_token:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: meta-ads-mcp
3
- Version: 1.0.12
3
+ Version: 1.0.15
4
4
  Summary: Model Context Protocol (MCP) server 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
@@ -25,12 +25,14 @@ Description-Content-Type: text/markdown
25
25
 
26
26
  # Meta Ads MCP
27
27
 
28
- A [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) server for interacting with Meta Ads API. This tool enables AI models to access, analyze, and manage Meta advertising campaigns through a standardized interface, allowing LLMs to retrieve performance data, visualize ad creatives, and provide strategic insights for Facebook, Instagram, and other Meta platforms.
28
+ A [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) server for interacting with Meta Ads. Analyze, manage and optimize Meta advertising campaigns through an AI interface. Use an LLM to retrieve performance data, visualize ad creatives, and provide strategic insights for your ads on Facebook, Instagram, and other Meta platforms.
29
29
 
30
30
  > **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.
31
31
 
32
32
  [![Meta Ads MCP Server Demo](https://github.com/user-attachments/assets/3e605cee-d289-414b-814c-6299e7f3383e)](https://github.com/user-attachments/assets/3e605cee-d289-414b-814c-6299e7f3383e)
33
33
 
34
+ [![MCP Badge](https://lobehub.com/badge/mcp/nictuku-meta-ads-mcp)](https://lobehub.com/mcp/nictuku-meta-ads-mcp)
35
+
34
36
  mcp-name: co.pipeboard/meta-ads-mcp
35
37
 
36
38
  ## Community & Support
@@ -1,17 +1,17 @@
1
- meta_ads_mcp/__init__.py,sha256=ISkIj_qzgBM_POGsJ9ri36jGbj_n3EKYWFebNXEV974,1477
1
+ meta_ads_mcp/__init__.py,sha256=CZF19i71cp1oW8UN66DGURoUuqAlgTmIkQBISUiNovY,1477
2
2
  meta_ads_mcp/__main__.py,sha256=XaQt3iXftG_7f0Zu7Wop9SeFgrD2WBn0EQOaPMc27d8,207
3
3
  meta_ads_mcp/core/__init__.py,sha256=IEJtqpyUo0CZSUWeQPljQ-D2vKorTFwXnpBQWSi1hIM,1819
4
4
  meta_ads_mcp/core/accounts.py,sha256=7Zoqq0zMIJi_Xsxe9-_b3EYx-UTeieJJvO7HxVRuUS0,4327
5
- meta_ads_mcp/core/ads.py,sha256=5ulQ4p3lLo1_sIiAoMdyeo0O2y7Yq2zSDuvqkNiqX1c,61376
5
+ meta_ads_mcp/core/ads.py,sha256=s-E_ZzgbsMbnv2xblaDFgazcKDJIPyYKY8VC2dkeMBQ,61595
6
6
  meta_ads_mcp/core/ads_library.py,sha256=smGz9FhM6RIUjlQT4Jv1BaZmXahGdK21eRCB7QMhK-4,3228
7
7
  meta_ads_mcp/core/adsets.py,sha256=3Ok3EwPTReKshtsVs4gRMlws6LMTUJTb4ZeGPPM8JR8,16570
8
- meta_ads_mcp/core/api.py,sha256=dR1h3sTnSmIdEz8OSttqELA0YxBkbjMnSflgM5LsJ1A,16858
8
+ meta_ads_mcp/core/api.py,sha256=RvEbXpVO_SxKJeuqsiQhpGYLZuhQlYKCd9hNv7AIZac,16856
9
9
  meta_ads_mcp/core/auth.py,sha256=l_IvejK2KYXg8yhBiP0ifE6mGwJ6ZujqYQbVw1KOUME,23649
10
- meta_ads_mcp/core/authentication.py,sha256=ftoKec1HpfYCCVYIVKUD3ezvVAk6n_CJlBuePn8fzpM,10547
10
+ meta_ads_mcp/core/authentication.py,sha256=bAdmlSxQpqFN1vup9Wijl-iCfPLPMyFfMhIPdoNOPYg,10540
11
11
  meta_ads_mcp/core/budget_schedules.py,sha256=FVyJuKbjUE4cmtlPJEbIwpN6JmU-1WQjc7io1y5JaHE,2911
12
12
  meta_ads_mcp/core/callback_server.py,sha256=LIAJv9DW--83kdZ7VWWZal8xEprYjRZ8iug4rMczYbQ,9372
13
13
  meta_ads_mcp/core/campaigns.py,sha256=m24epO1QmyBVBXfqHIFZAi6sRCTlOGLQckjy0azqkvo,14319
14
- meta_ads_mcp/core/duplication.py,sha256=ae9GisFg9MMXbgX8zqb6ekEFIv7DGNCMV-lwkDXSL-c,18928
14
+ meta_ads_mcp/core/duplication.py,sha256=pJ41pI9bIxNf21TSy6KkpcUsc3aSr4KIiO__sunNoq0,18909
15
15
  meta_ads_mcp/core/http_auth_integration.py,sha256=lGpKhfzJcyWugBcYEvypY-qnlt-3UDBLqh7xAUH0DGw,12473
16
16
  meta_ads_mcp/core/insights.py,sha256=ogSGlC0ddh8RFzWydilfDc7GYnjtInFiEQS7cSoyTmw,5061
17
17
  meta_ads_mcp/core/openai_deep_research.py,sha256=68ayGopnBSPEYhN9R1sFvTXtyWtM0lji9aWS3uSXnLY,18649
@@ -21,8 +21,8 @@ meta_ads_mcp/core/resources.py,sha256=-zIIfZulpo76vcKv6jhAlQq91cR2SZ3cjYZt3ek3x0
21
21
  meta_ads_mcp/core/server.py,sha256=9SlgM_qvdlxo24ctnZzLgW1e1nfAspCSx3YyJQkKP64,17856
22
22
  meta_ads_mcp/core/targeting.py,sha256=d2uLWbIEtucRuTgwZEdtVKLDZJgaxQ1lDtZ0ZgkBJC4,25150
23
23
  meta_ads_mcp/core/utils.py,sha256=ytj41yC5SqduLrAiZYBSd6OUwlJRaIClTwnnYKpNFds,9387
24
- meta_ads_mcp-1.0.12.dist-info/METADATA,sha256=5IsGuljH-ljFx_XOlBhXieKk9P8q_MwazodK4qTj5MQ,24289
25
- meta_ads_mcp-1.0.12.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
26
- meta_ads_mcp-1.0.12.dist-info/entry_points.txt,sha256=Dv2RkoBjRJBqj6CyhwqGIiwPCD-SCL1-7B9-zmVRuv0,57
27
- meta_ads_mcp-1.0.12.dist-info/licenses/LICENSE,sha256=E2d762fbhwKRYn8o7J6Szr6vyBPrHVDlK3jbHPx-d84,3851
28
- meta_ads_mcp-1.0.12.dist-info/RECORD,,
24
+ meta_ads_mcp-1.0.15.dist-info/METADATA,sha256=cZhvlDIsRWU3a0JDj_HkV3oWa079qBrvcOmoC9RPZGw,24369
25
+ meta_ads_mcp-1.0.15.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
26
+ meta_ads_mcp-1.0.15.dist-info/entry_points.txt,sha256=Dv2RkoBjRJBqj6CyhwqGIiwPCD-SCL1-7B9-zmVRuv0,57
27
+ meta_ads_mcp-1.0.15.dist-info/licenses/LICENSE,sha256=E2d762fbhwKRYn8o7J6Szr6vyBPrHVDlK3jbHPx-d84,3851
28
+ meta_ads_mcp-1.0.15.dist-info/RECORD,,