meta-ads-mcp 0.2.6__py3-none-any.whl → 0.2.9__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.
@@ -1,7 +1,7 @@
1
1
  """Campaign-related functionality for Meta Ads API."""
2
2
 
3
3
  import json
4
- from typing import List, Optional
4
+ from typing import List, Optional, Dict, Any, Union
5
5
  from .api import meta_api_tool, make_api_request
6
6
  from .accounts import get_ad_accounts
7
7
  from .server import mcp_server
@@ -13,6 +13,12 @@ async def get_campaigns(access_token: str = None, account_id: str = None, limit:
13
13
  """
14
14
  Get campaigns for a Meta Ads account with optional filtering.
15
15
 
16
+ Note: By default, the Meta API returns a subset of available fields.
17
+ Other fields like 'effective_status', 'special_ad_categories',
18
+ 'lifetime_budget', 'spend_cap', 'budget_remaining', 'promoted_object',
19
+ 'source_campaign_id', etc., might be available but require specifying them
20
+ in the API call (currently not exposed by this tool's parameters).
21
+
16
22
  Args:
17
23
  access_token: Meta API access token (optional - will use cached token if not provided)
18
24
  account_id: Meta Ads account ID (format: act_XXXXXXXXX)
@@ -36,7 +42,7 @@ async def get_campaigns(access_token: str = None, account_id: str = None, limit:
36
42
  }
37
43
 
38
44
  if status_filter:
39
- params["effective_status"] = [status_filter]
45
+ params["effective_status"] = status_filter
40
46
 
41
47
  data = await make_api_request(endpoint, access_token, params)
42
48
 
@@ -48,6 +54,10 @@ async def get_campaigns(access_token: str = None, account_id: str = None, limit:
48
54
  async def get_campaign_details(access_token: str = None, campaign_id: str = None) -> str:
49
55
  """
50
56
  Get detailed information about a specific campaign.
57
+
58
+ Note: This function requests a specific set of fields ('id,name,objective,status,...').
59
+ The Meta API offers many other fields for campaigns (e.g., 'effective_status', 'source_campaign_id', etc.)
60
+ that could be added to the 'fields' parameter in the code if needed.
51
61
 
52
62
  Args:
53
63
  access_token: Meta API access token (optional - will use cached token if not provided)
@@ -75,8 +85,14 @@ async def create_campaign(
75
85
  objective: str = None,
76
86
  status: str = "PAUSED",
77
87
  special_ad_categories: List[str] = None,
78
- daily_budget: Optional[int] = None,
79
- lifetime_budget: Optional[int] = None
88
+ daily_budget = None,
89
+ lifetime_budget = None,
90
+ buying_type: str = None,
91
+ bid_strategy: str = None,
92
+ bid_cap = None,
93
+ spend_cap = None,
94
+ campaign_budget_optimization: bool = None,
95
+ ab_test_control_setups: Optional[List[Dict[str, Any]]] = None
80
96
  ) -> str:
81
97
  """
82
98
  Create a new campaign in a Meta Ads account.
@@ -88,8 +104,14 @@ async def create_campaign(
88
104
  objective: Campaign objective (AWARENESS, TRAFFIC, ENGAGEMENT, etc.)
89
105
  status: Initial campaign status (default: PAUSED)
90
106
  special_ad_categories: List of special ad categories if applicable
91
- daily_budget: Daily budget in account currency (in cents)
92
- lifetime_budget: Lifetime budget in account currency (in cents)
107
+ daily_budget: Daily budget in account currency (in cents) as a string
108
+ lifetime_budget: Lifetime budget in account currency (in cents) as a string
109
+ buying_type: Buying type (e.g., 'AUCTION')
110
+ bid_strategy: Bid strategy (e.g., 'LOWEST_COST', 'LOWEST_COST_WITH_BID_CAP', 'COST_CAP')
111
+ bid_cap: Bid cap in account currency (in cents) as a string
112
+ spend_cap: Spending limit for the campaign in account currency (in cents) as a string
113
+ campaign_budget_optimization: Whether to enable campaign budget optimization
114
+ ab_test_control_setups: Settings for A/B testing (e.g., [{"name":"Creative A", "ad_format":"SINGLE_IMAGE"}])
93
115
  """
94
116
  # Check required parameters
95
117
  if not account_id:
@@ -101,23 +123,138 @@ async def create_campaign(
101
123
  if not objective:
102
124
  return json.dumps({"error": "No campaign objective provided"}, indent=2)
103
125
 
126
+ # Special_ad_categories is required by the API, set default if not provided
127
+ if special_ad_categories is None:
128
+ special_ad_categories = []
129
+
130
+ # For this example, we'll add a fixed daily budget if none is provided
131
+ if not daily_budget and not lifetime_budget:
132
+ daily_budget = "1000" # Default to $10 USD
133
+
104
134
  endpoint = f"{account_id}/campaigns"
105
135
 
106
136
  params = {
107
137
  "name": name,
108
138
  "objective": objective,
109
139
  "status": status,
140
+ "special_ad_categories": json.dumps(special_ad_categories) # Properly format as JSON string
110
141
  }
111
142
 
112
- if special_ad_categories:
113
- params["special_ad_categories"] = special_ad_categories
143
+ # Convert budget values to strings if they aren't already
144
+ if daily_budget is not None:
145
+ params["daily_budget"] = str(daily_budget)
146
+
147
+ if lifetime_budget is not None:
148
+ params["lifetime_budget"] = str(lifetime_budget)
149
+
150
+ # Add new parameters
151
+ if buying_type:
152
+ params["buying_type"] = buying_type
153
+
154
+ if bid_strategy:
155
+ params["bid_strategy"] = bid_strategy
156
+
157
+ if bid_cap is not None:
158
+ params["bid_cap"] = str(bid_cap)
114
159
 
115
- if daily_budget:
116
- params["daily_budget"] = daily_budget
160
+ if spend_cap is not None:
161
+ params["spend_cap"] = str(spend_cap)
117
162
 
118
- if lifetime_budget:
119
- params["lifetime_budget"] = lifetime_budget
163
+ if campaign_budget_optimization is not None:
164
+ params["campaign_budget_optimization"] = "true" if campaign_budget_optimization else "false"
165
+
166
+ if ab_test_control_setups:
167
+ params["ab_test_control_setups"] = json.dumps(ab_test_control_setups)
168
+
169
+ try:
170
+ data = await make_api_request(endpoint, access_token, params, method="POST")
171
+ return json.dumps(data, indent=2)
172
+ except Exception as e:
173
+ error_msg = str(e)
174
+ return json.dumps({
175
+ "error": "Failed to create campaign",
176
+ "details": error_msg,
177
+ "params_sent": params
178
+ }, indent=2)
179
+
180
+
181
+ @mcp_server.tool()
182
+ @meta_api_tool
183
+ async def update_campaign(
184
+ access_token: str = None,
185
+ campaign_id: str = None,
186
+ name: str = None,
187
+ status: str = None,
188
+ special_ad_categories: List[str] = None,
189
+ daily_budget = None,
190
+ lifetime_budget = None,
191
+ bid_strategy: str = None,
192
+ bid_cap = None,
193
+ spend_cap = None,
194
+ campaign_budget_optimization: bool = None,
195
+ objective: str = None, # Add objective if it's updatable
196
+ # Add other updatable fields as needed based on API docs
197
+ ) -> str:
198
+ """
199
+ Update an existing campaign in a Meta Ads account.
200
+
201
+ Args:
202
+ access_token: Meta API access token (optional - will use cached token if not provided)
203
+ campaign_id: Meta Ads campaign ID (required)
204
+ name: New campaign name
205
+ status: New campaign status (e.g., 'ACTIVE', 'PAUSED')
206
+ special_ad_categories: List of special ad categories if applicable
207
+ daily_budget: New daily budget in account currency (in cents) as a string
208
+ lifetime_budget: New lifetime budget in account currency (in cents) as a string
209
+ bid_strategy: New bid strategy
210
+ bid_cap: New bid cap in account currency (in cents) as a string
211
+ spend_cap: New spending limit for the campaign in account currency (in cents) as a string
212
+ campaign_budget_optimization: Enable/disable campaign budget optimization
213
+ objective: New campaign objective (Note: May not always be updatable)
214
+ """
215
+ if not campaign_id:
216
+ return json.dumps({"error": "No campaign ID provided"}, indent=2)
217
+
218
+ endpoint = f"{campaign_id}"
120
219
 
121
- data = await make_api_request(endpoint, access_token, params, method="POST")
220
+ params = {}
122
221
 
123
- return json.dumps(data, indent=2)
222
+ # Add parameters to the request only if they are provided
223
+ if name is not None:
224
+ params["name"] = name
225
+ if status is not None:
226
+ params["status"] = status
227
+ if special_ad_categories is not None:
228
+ # Note: Updating special_ad_categories might have specific API rules or might not be allowed after creation.
229
+ # The API might require an empty list `[]` to clear categories. Check Meta Docs.
230
+ params["special_ad_categories"] = json.dumps(special_ad_categories)
231
+ if daily_budget is not None:
232
+ params["daily_budget"] = str(daily_budget)
233
+ if lifetime_budget is not None:
234
+ params["lifetime_budget"] = str(lifetime_budget)
235
+ if bid_strategy is not None:
236
+ params["bid_strategy"] = bid_strategy
237
+ if bid_cap is not None:
238
+ params["bid_cap"] = str(bid_cap)
239
+ if spend_cap is not None:
240
+ params["spend_cap"] = str(spend_cap)
241
+ if campaign_budget_optimization is not None:
242
+ params["campaign_budget_optimization"] = "true" if campaign_budget_optimization else "false"
243
+ if objective is not None:
244
+ params["objective"] = objective # Caution: Objective changes might reset learning or be restricted
245
+
246
+ if not params:
247
+ return json.dumps({"error": "No update parameters provided"}, indent=2)
248
+
249
+ try:
250
+ # Use POST method for updates as per Meta API documentation
251
+ data = await make_api_request(endpoint, access_token, params, method="POST")
252
+ return json.dumps(data, indent=2)
253
+ except Exception as e:
254
+ error_msg = str(e)
255
+ # Include campaign_id in error for better context
256
+ return json.dumps({
257
+ "error": f"Failed to update campaign {campaign_id}",
258
+ "details": error_msg,
259
+ "params_sent": params # Be careful about logging sensitive data if any
260
+ }, indent=2)
@@ -1,7 +1,7 @@
1
1
  """Insights and Reporting functionality for Meta Ads API."""
2
2
 
3
3
  import json
4
- from typing import Optional
4
+ from typing import Optional, Union, Dict
5
5
  from .api import meta_api_tool, make_api_request
6
6
  from .utils import download_image, try_multiple_download_methods, ad_creative_images, create_resource_from_image
7
7
  from .server import mcp_server
@@ -12,7 +12,7 @@ import datetime
12
12
  @mcp_server.tool()
13
13
  @meta_api_tool
14
14
  async def get_insights(access_token: str = None, object_id: str = None,
15
- time_range: str = "maximum", breakdown: str = "",
15
+ time_range: Union[str, Dict[str, str]] = "maximum", breakdown: str = "",
16
16
  level: str = "ad") -> str:
17
17
  """
18
18
  Get performance insights for a campaign, ad set, ad or account.
@@ -20,7 +20,11 @@ async def get_insights(access_token: str = None, object_id: str = None,
20
20
  Args:
21
21
  access_token: Meta API access token (optional - will use cached token if not provided)
22
22
  object_id: ID of the campaign, ad set, ad or account
23
- time_range: Time range for insights (default: last_30_days, options: today, yesterday, this_month, last_month, this_quarter, maximum, data_maximum, last_3d, last_7d, last_14d, last_28d, last_30d, last_90d, last_week_mon_sun, last_week_sun_sat, last_quarter, last_year, this_week_mon_today, this_week_sun_today, this_year)
23
+ time_range: Either a preset time range string or a dictionary with "since" and "until" dates in YYYY-MM-DD format
24
+ Preset options: today, yesterday, this_month, last_month, this_quarter, maximum, data_maximum,
25
+ last_3d, last_7d, last_14d, last_28d, last_30d, last_90d, last_week_mon_sun,
26
+ last_week_sun_sat, last_quarter, last_year, this_week_mon_today, this_week_sun_today, this_year
27
+ Dictionary example: {"since":"2023-01-01","until":"2023-01-31"}
24
28
  breakdown: Optional breakdown dimension (e.g., age, gender, country)
25
29
  level: Level of aggregation (ad, adset, campaign, account)
26
30
  """
@@ -29,11 +33,21 @@ async def get_insights(access_token: str = None, object_id: str = None,
29
33
 
30
34
  endpoint = f"{object_id}/insights"
31
35
  params = {
32
- "date_preset": time_range,
33
36
  "fields": "account_id,account_name,campaign_id,campaign_name,adset_id,adset_name,ad_id,ad_name,impressions,clicks,spend,cpc,cpm,ctr,reach,frequency,actions,conversions,unique_clicks,cost_per_action_type",
34
37
  "level": level
35
38
  }
36
39
 
40
+ # Handle time range based on type
41
+ if isinstance(time_range, dict):
42
+ # Use custom date range with since/until parameters
43
+ if "since" in time_range and "until" in time_range:
44
+ params["time_range"] = json.dumps(time_range)
45
+ else:
46
+ return json.dumps({"error": "Custom time_range must contain both 'since' and 'until' keys in YYYY-MM-DD format"}, indent=2)
47
+ else:
48
+ # Use preset date range
49
+ params["date_preset"] = time_range
50
+
37
51
  if breakdown:
38
52
  params["breakdowns"] = breakdown
39
53