meta-ads-mcp 0.9.1__py3-none-any.whl → 0.9.3__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.9.1"
10
+ __version__ = "0.9.3"
11
11
 
12
12
  __all__ = [
13
13
  'get_ad_accounts',
@@ -8,14 +8,14 @@ from .server import mcp_server
8
8
 
9
9
  @mcp_server.tool()
10
10
  @meta_api_tool
11
- async def get_ad_accounts(access_token: str = None, user_id: str = "me", limit: int = 10) -> str:
11
+ async def get_ad_accounts(access_token: str = None, user_id: str = "me", limit: int = 200) -> str:
12
12
  """
13
13
  Get ad accounts accessible by a user.
14
14
 
15
15
  Args:
16
16
  access_token: Meta API access token (optional - will use cached token if not provided)
17
17
  user_id: Meta user ID or "me" for the current user
18
- limit: Maximum number of accounts to return (default: 10)
18
+ limit: Maximum number of accounts to return (default: 200)
19
19
  """
20
20
  endpoint = f"{user_id}/adaccounts"
21
21
  params = {
@@ -51,32 +51,7 @@ async def get_account_info(access_token: str = None, account_id: str = None) ->
51
51
  if not account_id.startswith("act_"):
52
52
  account_id = f"act_{account_id}"
53
53
 
54
- # First, check if the account is accessible to the user
55
- endpoint = "me/adaccounts"
56
- params = {
57
- "fields": "id,name,account_id,account_status,amount_spent,balance,currency,age,business_city,business_country_code",
58
- "limit": 50
59
- }
60
- accessible_accounts_data = await make_api_request(endpoint, access_token, params)
61
-
62
- if "data" in accessible_accounts_data:
63
- accessible_account_ids = [acc["id"] for acc in accessible_accounts_data["data"]]
64
- if account_id not in accessible_account_ids:
65
- # Provide a helpful error message with accessible accounts
66
- accessible_accounts = [
67
- {"id": acc["id"], "name": acc["name"]}
68
- for acc in accessible_accounts_data["data"][:10] # Show first 10
69
- ]
70
- return {
71
- "error": {
72
- "message": f"Account {account_id} is not accessible to your user account",
73
- "details": "This account either doesn't exist or you don't have permission to access it",
74
- "accessible_accounts": accessible_accounts,
75
- "total_accessible_accounts": len(accessible_accounts_data["data"]),
76
- "suggestion": "Try using one of the accessible account IDs listed above"
77
- }
78
- }
79
-
54
+ # Try to get the account info directly first
80
55
  endpoint = f"{account_id}"
81
56
  params = {
82
57
  "fields": "id,name,account_id,account_status,amount_spent,balance,currency,age,business_city,business_country_code,timezone_name"
@@ -86,6 +61,32 @@ async def get_account_info(access_token: str = None, account_id: str = None) ->
86
61
 
87
62
  # Check if the API request returned an error
88
63
  if "error" in data:
64
+ # If access was denied, provide helpful error message with accessible accounts
65
+ if "access" in str(data.get("error", {})).lower() or "permission" in str(data.get("error", {})).lower():
66
+ # Get list of accessible accounts for helpful error message
67
+ accessible_endpoint = "me/adaccounts"
68
+ accessible_params = {
69
+ "fields": "id,name,account_id,account_status,amount_spent,balance,currency,age,business_city,business_country_code",
70
+ "limit": 50
71
+ }
72
+ accessible_accounts_data = await make_api_request(accessible_endpoint, access_token, accessible_params)
73
+
74
+ if "data" in accessible_accounts_data:
75
+ accessible_accounts = [
76
+ {"id": acc["id"], "name": acc["name"]}
77
+ for acc in accessible_accounts_data["data"][:10] # Show first 10
78
+ ]
79
+ return {
80
+ "error": {
81
+ "message": f"Account {account_id} is not accessible to your user account",
82
+ "details": "This account either doesn't exist or you don't have permission to access it",
83
+ "accessible_accounts": accessible_accounts,
84
+ "total_accessible_accounts": len(accessible_accounts_data["data"]),
85
+ "suggestion": "Try using one of the accessible account IDs listed above"
86
+ }
87
+ }
88
+
89
+ # Return the original error for non-permission related issues
89
90
  return data
90
91
 
91
92
  # Add DSA requirement detection
@@ -104,6 +104,8 @@ async def create_adset(
104
104
  start_time: str = None,
105
105
  end_time: str = None,
106
106
  dsa_beneficiary: str = None,
107
+ promoted_object: Dict[str, Any] = None,
108
+ destination_type: str = None,
107
109
  access_token: str = None
108
110
  ) -> str:
109
111
  """
@@ -118,13 +120,18 @@ async def create_adset(
118
120
  lifetime_budget: Lifetime budget in account currency (in cents) as a string
119
121
  targeting: Targeting specifications including age, location, interests, etc.
120
122
  Use targeting_automation.advantage_audience=1 for automatic audience finding
121
- optimization_goal: Conversion optimization goal (e.g., 'LINK_CLICKS', 'REACH', 'CONVERSIONS')
123
+ optimization_goal: Conversion optimization goal (e.g., 'LINK_CLICKS', 'REACH', 'CONVERSIONS', 'APP_INSTALLS')
122
124
  billing_event: How you're charged (e.g., 'IMPRESSIONS', 'LINK_CLICKS')
123
125
  bid_amount: Bid amount in account currency (in cents)
124
126
  bid_strategy: Bid strategy (e.g., 'LOWEST_COST', 'LOWEST_COST_WITH_BID_CAP')
125
127
  start_time: Start time in ISO 8601 format (e.g., '2023-12-01T12:00:00-0800')
126
128
  end_time: End time in ISO 8601 format
127
129
  dsa_beneficiary: DSA beneficiary (person/organization benefiting from ads) for European compliance
130
+ promoted_object: Mobile app configuration for APP_INSTALLS campaigns. Required fields: application_id, object_store_url.
131
+ Optional fields: custom_event_type, pixel_id, page_id.
132
+ Example: {"application_id": "123456789012345", "object_store_url": "https://apps.apple.com/app/id123456789"}
133
+ destination_type: Where users are directed after clicking the ad (e.g., 'APP_STORE', 'DEEPLINK', 'APP_INSTALL').
134
+ Required for mobile app campaigns.
128
135
  access_token: Meta API access token (optional - will use cached token if not provided)
129
136
  """
130
137
  # Check required parameters
@@ -143,6 +150,59 @@ async def create_adset(
143
150
  if not billing_event:
144
151
  return json.dumps({"error": "No billing event provided"}, indent=2)
145
152
 
153
+ # Validate mobile app parameters for APP_INSTALLS campaigns
154
+ if optimization_goal == "APP_INSTALLS":
155
+ if not promoted_object:
156
+ return json.dumps({
157
+ "error": "promoted_object is required for APP_INSTALLS optimization goal",
158
+ "details": "Mobile app campaigns must specify which app is being promoted",
159
+ "required_fields": ["application_id", "object_store_url"]
160
+ }, indent=2)
161
+
162
+ # Validate promoted_object structure
163
+ if not isinstance(promoted_object, dict):
164
+ return json.dumps({
165
+ "error": "promoted_object must be a dictionary",
166
+ "example": {"application_id": "123456789012345", "object_store_url": "https://apps.apple.com/app/id123456789"}
167
+ }, indent=2)
168
+
169
+ # Validate required promoted_object fields
170
+ if "application_id" not in promoted_object:
171
+ return json.dumps({
172
+ "error": "promoted_object missing required field: application_id",
173
+ "details": "application_id is the Facebook app ID for your mobile app"
174
+ }, indent=2)
175
+
176
+ if "object_store_url" not in promoted_object:
177
+ return json.dumps({
178
+ "error": "promoted_object missing required field: object_store_url",
179
+ "details": "object_store_url should be the App Store or Google Play URL for your app"
180
+ }, indent=2)
181
+
182
+ # Validate store URL format
183
+ store_url = promoted_object["object_store_url"]
184
+ valid_store_patterns = [
185
+ "apps.apple.com", # iOS App Store
186
+ "play.google.com", # Google Play Store
187
+ "itunes.apple.com" # Alternative iOS format
188
+ ]
189
+
190
+ if not any(pattern in store_url for pattern in valid_store_patterns):
191
+ return json.dumps({
192
+ "error": "Invalid object_store_url format",
193
+ "details": "URL must be from App Store (apps.apple.com) or Google Play (play.google.com)",
194
+ "provided_url": store_url
195
+ }, indent=2)
196
+
197
+ # Validate destination_type if provided
198
+ if destination_type:
199
+ valid_destination_types = ["APP_STORE", "DEEPLINK", "APP_INSTALL"]
200
+ if destination_type not in valid_destination_types:
201
+ return json.dumps({
202
+ "error": f"Invalid destination_type: {destination_type}",
203
+ "valid_values": valid_destination_types
204
+ }, indent=2)
205
+
146
206
  # Basic targeting is required if not provided
147
207
  if not targeting:
148
208
  targeting = {
@@ -187,6 +247,13 @@ async def create_adset(
187
247
  if dsa_beneficiary:
188
248
  params["dsa_beneficiary"] = dsa_beneficiary
189
249
 
250
+ # Add mobile app parameters if provided
251
+ if promoted_object:
252
+ params["promoted_object"] = json.dumps(promoted_object)
253
+
254
+ if destination_type:
255
+ params["destination_type"] = destination_type
256
+
190
257
  try:
191
258
  data = await make_api_request(endpoint, access_token, params, method="POST")
192
259
  return json.dumps(data, indent=2)
@@ -23,7 +23,7 @@ class MetaAdsDataManager:
23
23
  self._cache = {}
24
24
  logger.debug("MetaAdsDataManager initialized")
25
25
 
26
- async def _get_ad_accounts(self, access_token: str, limit: int = 25) -> List[Dict[str, Any]]:
26
+ async def _get_ad_accounts(self, access_token: str, limit: int = 200) -> List[Dict[str, Any]]:
27
27
  """Get ad accounts data"""
28
28
  try:
29
29
  endpoint = "me/adaccounts"
@@ -141,7 +141,7 @@ class MetaAdsDataManager:
141
141
 
142
142
  try:
143
143
  # Search ad accounts
144
- accounts = await self._get_ad_accounts(access_token, limit=25)
144
+ accounts = await self._get_ad_accounts(access_token, limit=200)
145
145
  for account in accounts:
146
146
  account_text = f"{account.get('name', '')} {account.get('id', '')} {account.get('account_status', '')} {account.get('business_city', '')} {account.get('business_country_code', '')}".lower()
147
147
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: meta-ads-mcp
3
- Version: 0.9.1
3
+ Version: 0.9.3
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
@@ -136,7 +136,7 @@ For local installation configuration, authentication options, and advanced techn
136
136
  - Inputs:
137
137
  - `access_token` (optional): Meta API access token (will use cached token if not provided)
138
138
  - `user_id`: Meta user ID or "me" for the current user
139
- - `limit`: Maximum number of accounts to return (default: 10)
139
+ - `limit`: Maximum number of accounts to return (default: 200)
140
140
  - Returns: List of accessible ad accounts with their details
141
141
 
142
142
  2. `mcp_meta_ads_get_account_info`
@@ -1,10 +1,10 @@
1
- meta_ads_mcp/__init__.py,sha256=cuoBTLMG08AS3jOGl3ZE2shLXAQezYBs5HNzSZlmM9Q,1492
1
+ meta_ads_mcp/__init__.py,sha256=XPRyVV6Kg7dg4tJuy0N5Kwwf25oVRhKRRz9rw5fi3x4,1492
2
2
  meta_ads_mcp/__main__.py,sha256=XaQt3iXftG_7f0Zu7Wop9SeFgrD2WBn0EQOaPMc27d8,207
3
3
  meta_ads_mcp/core/__init__.py,sha256=6nYdue6yRepkt6JTAoPGhGbS51qfDSvmczRrDwYOG6A,1709
4
- meta_ads_mcp/core/accounts.py,sha256=4IAdGLZ4WE4j4pGW6E0qaXcXqbUIW6Wk2kuQUtlmRTQ,4030
4
+ meta_ads_mcp/core/accounts.py,sha256=0lfyvRbhBigFKgNq5Mk3fk4Qh6MtIkKScdOnoc_rQkg,4314
5
5
  meta_ads_mcp/core/ads.py,sha256=3UpoktML-5hJ_k2u5KOk3-VcFe_goz531jLi6-qJhqU,54929
6
6
  meta_ads_mcp/core/ads_library.py,sha256=BBGVbtjO5eFV42iiY3XPU-wIV8HupzUKpHgPBrydSvU,3232
7
- meta_ads_mcp/core/adsets.py,sha256=vY5JNHmGK1a_sQ5B1LnjxLYXzs5_jOajTTjWHRDJ4_Y,12518
7
+ meta_ads_mcp/core/adsets.py,sha256=mqB7u-6HY3QHSQo-zStan7IBjksdcvzcyOC_wVXFvJY,15863
8
8
  meta_ads_mcp/core/api.py,sha256=kWpIafvSsxnesfb5TqndA7ozKoIspby5e_6Jl23L7hY,16447
9
9
  meta_ads_mcp/core/auth.py,sha256=2CjFbxpJM3OR3OzCipB8l_-l2xQ1nioGfdI3ZDMnjHM,23629
10
10
  meta_ads_mcp/core/authentication.py,sha256=-AJxa3a5ZshRCvmJThBaNwCAJ1D2_qOgUkvu539c_MY,10159
@@ -14,15 +14,15 @@ meta_ads_mcp/core/campaigns.py,sha256=0yDVgi7rN4eMQk1_w0A2vnoXd8y0t8R77Ji4gna1Gj
14
14
  meta_ads_mcp/core/duplication.py,sha256=UUmTDFx9o5ZsPQG2Rb9c4ZyuKUVN3FfTjebfTIHHdo4,18984
15
15
  meta_ads_mcp/core/http_auth_integration.py,sha256=lGpKhfzJcyWugBcYEvypY-qnlt-3UDBLqh7xAUH0DGw,12473
16
16
  meta_ads_mcp/core/insights.py,sha256=unhcCaYjgsir62llCdIDg0F-PHISiHune08uYG5IXTM,4707
17
- meta_ads_mcp/core/openai_deep_research.py,sha256=gLQlzIUBd-VyBMYjJ4vchMadQ-4aT3PEMqd9GLq6MYw,18805
17
+ meta_ads_mcp/core/openai_deep_research.py,sha256=9w3F2Mqj-hYOcvFlnG0tnALlW12VwavDMMpO7LqApd0,18807
18
18
  meta_ads_mcp/core/pipeboard_auth.py,sha256=ZwEQy8r0TwobFRQ5gmlSjhIfvlUmMtfWNlpQjXCUhl0,24582
19
19
  meta_ads_mcp/core/reports.py,sha256=Dv3hfsPOR7IZ9WrYrKd_6SNgZl-USIphg7knva3UYAw,5747
20
20
  meta_ads_mcp/core/resources.py,sha256=-zIIfZulpo76vcKv6jhAlQq91cR2SZ3cjYZt3ek3x0w,1236
21
21
  meta_ads_mcp/core/server.py,sha256=9SlgM_qvdlxo24ctnZzLgW1e1nfAspCSx3YyJQkKP64,17856
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.9.1.dist-info/METADATA,sha256=umFWyEzIi3QIGP89k27qH5XVOu26IAdfz4dKM8iX_pc,22443
25
- meta_ads_mcp-0.9.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
26
- meta_ads_mcp-0.9.1.dist-info/entry_points.txt,sha256=Dv2RkoBjRJBqj6CyhwqGIiwPCD-SCL1-7B9-zmVRuv0,57
27
- meta_ads_mcp-0.9.1.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
28
- meta_ads_mcp-0.9.1.dist-info/RECORD,,
24
+ meta_ads_mcp-0.9.3.dist-info/METADATA,sha256=BloabUTrmKx3ihtvvt9eugMXW8SC75fy2DA5c7cQAoc,22444
25
+ meta_ads_mcp-0.9.3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
26
+ meta_ads_mcp-0.9.3.dist-info/entry_points.txt,sha256=Dv2RkoBjRJBqj6CyhwqGIiwPCD-SCL1-7B9-zmVRuv0,57
27
+ meta_ads_mcp-0.9.3.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
28
+ meta_ads_mcp-0.9.3.dist-info/RECORD,,