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.
- meta_ads_mcp/__init__.py +1 -1
- meta_ads_mcp/api.py +122 -53
- meta_ads_mcp/core/__init__.py +2 -1
- meta_ads_mcp/core/ads.py +123 -192
- meta_ads_mcp/core/adsets.py +137 -11
- meta_ads_mcp/core/api.py +29 -1
- meta_ads_mcp/core/auth.py +83 -15
- meta_ads_mcp/core/authentication.py +103 -49
- meta_ads_mcp/core/campaigns.py +151 -14
- meta_ads_mcp/core/insights.py +18 -4
- meta_ads_mcp/core/pipeboard_auth.py +484 -0
- meta_ads_mcp/core/server.py +49 -4
- meta_ads_mcp/core/utils.py +11 -5
- {meta_ads_mcp-0.2.6.dist-info → meta_ads_mcp-0.2.9.dist-info}/METADATA +122 -31
- meta_ads_mcp-0.2.9.dist-info/RECORD +22 -0
- meta_ads_mcp-0.2.9.dist-info/licenses/LICENSE +201 -0
- meta_ads_mcp-0.2.6.dist-info/RECORD +0 -20
- {meta_ads_mcp-0.2.6.dist-info → meta_ads_mcp-0.2.9.dist-info}/WHEEL +0 -0
- {meta_ads_mcp-0.2.6.dist-info → meta_ads_mcp-0.2.9.dist-info}/entry_points.txt +0 -0
meta_ads_mcp/core/campaigns.py
CHANGED
|
@@ -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"] =
|
|
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
|
|
79
|
-
lifetime_budget
|
|
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
|
|
113
|
-
|
|
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
|
|
116
|
-
params["
|
|
160
|
+
if spend_cap is not None:
|
|
161
|
+
params["spend_cap"] = str(spend_cap)
|
|
117
162
|
|
|
118
|
-
if
|
|
119
|
-
params["
|
|
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
|
-
|
|
220
|
+
params = {}
|
|
122
221
|
|
|
123
|
-
|
|
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)
|
meta_ads_mcp/core/insights.py
CHANGED
|
@@ -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:
|
|
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
|
|