meta-ads-mcp 0.7.5__tar.gz → 0.7.7__tar.gz
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-0.7.5 → meta_ads_mcp-0.7.7}/PKG-INFO +1 -1
- {meta_ads_mcp-0.7.5 → meta_ads_mcp-0.7.7}/RELEASE.md +23 -10
- {meta_ads_mcp-0.7.5 → meta_ads_mcp-0.7.7}/meta_ads_mcp/__init__.py +1 -1
- {meta_ads_mcp-0.7.5 → meta_ads_mcp-0.7.7}/meta_ads_mcp/core/accounts.py +10 -0
- {meta_ads_mcp-0.7.5 → meta_ads_mcp-0.7.7}/meta_ads_mcp/core/adsets.py +36 -6
- {meta_ads_mcp-0.7.5 → meta_ads_mcp-0.7.7}/meta_ads_mcp/core/insights.py +1 -1
- {meta_ads_mcp-0.7.5 → meta_ads_mcp-0.7.7}/pyproject.toml +1 -1
- meta_ads_mcp-0.7.7/tests/test_dsa_beneficiary.py +597 -0
- meta_ads_mcp-0.7.7/tests/test_dsa_integration.py +380 -0
- meta_ads_mcp-0.7.7/tests/test_insights_actions_and_values.py +488 -0
- {meta_ads_mcp-0.7.5 → meta_ads_mcp-0.7.7}/.github/workflows/publish.yml +0 -0
- {meta_ads_mcp-0.7.5 → meta_ads_mcp-0.7.7}/.github/workflows/test.yml +0 -0
- {meta_ads_mcp-0.7.5 → meta_ads_mcp-0.7.7}/.gitignore +0 -0
- {meta_ads_mcp-0.7.5 → meta_ads_mcp-0.7.7}/CUSTOM_META_APP.md +0 -0
- {meta_ads_mcp-0.7.5 → meta_ads_mcp-0.7.7}/Dockerfile +0 -0
- {meta_ads_mcp-0.7.5 → meta_ads_mcp-0.7.7}/LICENSE +0 -0
- {meta_ads_mcp-0.7.5 → meta_ads_mcp-0.7.7}/LOCAL_INSTALLATION.md +0 -0
- {meta_ads_mcp-0.7.5 → meta_ads_mcp-0.7.7}/META_API_NOTES.md +0 -0
- {meta_ads_mcp-0.7.5 → meta_ads_mcp-0.7.7}/README.md +0 -0
- {meta_ads_mcp-0.7.5 → meta_ads_mcp-0.7.7}/STREAMABLE_HTTP_SETUP.md +0 -0
- {meta_ads_mcp-0.7.5 → meta_ads_mcp-0.7.7}/examples/README.md +0 -0
- {meta_ads_mcp-0.7.5 → meta_ads_mcp-0.7.7}/examples/example_http_client.py +0 -0
- {meta_ads_mcp-0.7.5 → meta_ads_mcp-0.7.7}/future_improvements.md +0 -0
- {meta_ads_mcp-0.7.5 → meta_ads_mcp-0.7.7}/images/meta-ads-example.png +0 -0
- {meta_ads_mcp-0.7.5 → meta_ads_mcp-0.7.7}/meta_ads_auth.sh +0 -0
- {meta_ads_mcp-0.7.5 → meta_ads_mcp-0.7.7}/meta_ads_mcp/__main__.py +0 -0
- {meta_ads_mcp-0.7.5 → meta_ads_mcp-0.7.7}/meta_ads_mcp/core/__init__.py +0 -0
- {meta_ads_mcp-0.7.5 → meta_ads_mcp-0.7.7}/meta_ads_mcp/core/ads.py +0 -0
- {meta_ads_mcp-0.7.5 → meta_ads_mcp-0.7.7}/meta_ads_mcp/core/ads_library.py +0 -0
- {meta_ads_mcp-0.7.5 → meta_ads_mcp-0.7.7}/meta_ads_mcp/core/api.py +0 -0
- {meta_ads_mcp-0.7.5 → meta_ads_mcp-0.7.7}/meta_ads_mcp/core/auth.py +0 -0
- {meta_ads_mcp-0.7.5 → meta_ads_mcp-0.7.7}/meta_ads_mcp/core/authentication.py +0 -0
- {meta_ads_mcp-0.7.5 → meta_ads_mcp-0.7.7}/meta_ads_mcp/core/budget_schedules.py +0 -0
- {meta_ads_mcp-0.7.5 → meta_ads_mcp-0.7.7}/meta_ads_mcp/core/callback_server.py +0 -0
- {meta_ads_mcp-0.7.5 → meta_ads_mcp-0.7.7}/meta_ads_mcp/core/campaigns.py +0 -0
- {meta_ads_mcp-0.7.5 → meta_ads_mcp-0.7.7}/meta_ads_mcp/core/duplication.py +0 -0
- {meta_ads_mcp-0.7.5 → meta_ads_mcp-0.7.7}/meta_ads_mcp/core/http_auth_integration.py +0 -0
- {meta_ads_mcp-0.7.5 → meta_ads_mcp-0.7.7}/meta_ads_mcp/core/openai_deep_research.py +0 -0
- {meta_ads_mcp-0.7.5 → meta_ads_mcp-0.7.7}/meta_ads_mcp/core/pipeboard_auth.py +0 -0
- {meta_ads_mcp-0.7.5 → meta_ads_mcp-0.7.7}/meta_ads_mcp/core/reports.py +0 -0
- {meta_ads_mcp-0.7.5 → meta_ads_mcp-0.7.7}/meta_ads_mcp/core/resources.py +0 -0
- {meta_ads_mcp-0.7.5 → meta_ads_mcp-0.7.7}/meta_ads_mcp/core/server.py +0 -0
- {meta_ads_mcp-0.7.5 → meta_ads_mcp-0.7.7}/meta_ads_mcp/core/targeting.py +0 -0
- {meta_ads_mcp-0.7.5 → meta_ads_mcp-0.7.7}/meta_ads_mcp/core/utils.py +0 -0
- {meta_ads_mcp-0.7.5 → meta_ads_mcp-0.7.7}/requirements.txt +0 -0
- {meta_ads_mcp-0.7.5 → meta_ads_mcp-0.7.7}/setup.py +0 -0
- {meta_ads_mcp-0.7.5 → meta_ads_mcp-0.7.7}/smithery.yaml +0 -0
- {meta_ads_mcp-0.7.5 → meta_ads_mcp-0.7.7}/tests/README.md +0 -0
- {meta_ads_mcp-0.7.5 → meta_ads_mcp-0.7.7}/tests/README_REGRESSION_TESTS.md +0 -0
- {meta_ads_mcp-0.7.5 → meta_ads_mcp-0.7.7}/tests/__init__.py +0 -0
- {meta_ads_mcp-0.7.5 → meta_ads_mcp-0.7.7}/tests/conftest.py +0 -0
- {meta_ads_mcp-0.7.5 → meta_ads_mcp-0.7.7}/tests/test_account_search.py +0 -0
- {meta_ads_mcp-0.7.5 → meta_ads_mcp-0.7.7}/tests/test_budget_update.py +0 -0
- {meta_ads_mcp-0.7.5 → meta_ads_mcp-0.7.7}/tests/test_budget_update_e2e.py +0 -0
- {meta_ads_mcp-0.7.5 → meta_ads_mcp-0.7.7}/tests/test_duplication.py +0 -0
- {meta_ads_mcp-0.7.5 → meta_ads_mcp-0.7.7}/tests/test_duplication_regression.py +0 -0
- {meta_ads_mcp-0.7.5 → meta_ads_mcp-0.7.7}/tests/test_get_ad_creatives_fix.py +0 -0
- {meta_ads_mcp-0.7.5 → meta_ads_mcp-0.7.7}/tests/test_get_ad_image_regression.py +0 -0
- {meta_ads_mcp-0.7.5 → meta_ads_mcp-0.7.7}/tests/test_http_transport.py +0 -0
- {meta_ads_mcp-0.7.5 → meta_ads_mcp-0.7.7}/tests/test_integration_openai_mcp.py +0 -0
- {meta_ads_mcp-0.7.5 → meta_ads_mcp-0.7.7}/tests/test_openai.py +0 -0
- {meta_ads_mcp-0.7.5 → meta_ads_mcp-0.7.7}/tests/test_openai_mcp_deep_research.py +0 -0
- {meta_ads_mcp-0.7.5 → meta_ads_mcp-0.7.7}/tests/test_targeting.py +0 -0
- {meta_ads_mcp-0.7.5 → meta_ads_mcp-0.7.7}/tests/test_targeting_search_e2e.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: meta-ads-mcp
|
|
3
|
-
Version: 0.7.
|
|
3
|
+
Version: 0.7.7
|
|
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
|
|
@@ -29,15 +29,27 @@ This repository uses GitHub Actions to automatically publish releases to PyPI. H
|
|
|
29
29
|
git push origin main
|
|
30
30
|
```
|
|
31
31
|
|
|
32
|
-
3. **
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
32
|
+
3. **Wait for build tests to pass** (optional):
|
|
33
|
+
```bash
|
|
34
|
+
# Check the latest test workflow run
|
|
35
|
+
gh run list --workflow=test.yml --limit 1
|
|
36
|
+
|
|
37
|
+
# Get the run ID and wait for completion
|
|
38
|
+
RUN_ID=$(gh run list --workflow=test.yml --limit 1 --json databaseId --jq '.[0].databaseId')
|
|
39
|
+
gh run watch $RUN_ID
|
|
40
|
+
```
|
|
41
|
+
Note: This only tests package building and installation, not the actual pytest tests.
|
|
42
|
+
|
|
43
|
+
4. **Create a GitHub release**:
|
|
44
|
+
```bash
|
|
45
|
+
gh release create 0.3.8 --title "0.3.8" --generate-notes
|
|
46
|
+
```
|
|
47
|
+
This command will:
|
|
48
|
+
- Create a release with the specified version (no "v" prefix)
|
|
49
|
+
- Auto-generate release notes from commits
|
|
50
|
+
- Automatically trigger the GitHub Action for PyPI publishing
|
|
51
|
+
|
|
52
|
+
5. **Automatic deployment**:
|
|
41
53
|
- The GitHub Action will automatically trigger
|
|
42
54
|
- It will build the package and publish to PyPI
|
|
43
55
|
- Check the "Actions" tab to monitor progress
|
|
@@ -49,10 +61,11 @@ This repository uses GitHub Actions to automatically publish releases to PyPI. H
|
|
|
49
61
|
- **Purpose**: Builds and publishes the package to PyPI
|
|
50
62
|
- **Security**: Uses trusted publishing with OIDC tokens (no API keys needed)
|
|
51
63
|
|
|
52
|
-
### `test.yml`
|
|
64
|
+
### `test.yml`
|
|
53
65
|
- **Triggers**: On pushes and pull requests to main/master
|
|
54
66
|
- **Purpose**: Tests package building and installation across Python versions
|
|
55
67
|
- **Matrix**: Tests Python 3.10, 3.11, and 3.12
|
|
68
|
+
- **Note**: Does not run pytest tests, only validates package structure
|
|
56
69
|
|
|
57
70
|
## Manual Deployment
|
|
58
71
|
|
|
@@ -59,4 +59,14 @@ async def get_account_info(access_token: str = None, account_id: str = None) ->
|
|
|
59
59
|
|
|
60
60
|
data = await make_api_request(endpoint, access_token, params)
|
|
61
61
|
|
|
62
|
+
# Add DSA requirement detection
|
|
63
|
+
if "business_country_code" in data:
|
|
64
|
+
european_countries = ["DE", "FR", "IT", "ES", "NL", "BE", "AT", "IE", "DK", "SE", "FI", "NO"]
|
|
65
|
+
if data["business_country_code"] in european_countries:
|
|
66
|
+
data["dsa_required"] = True
|
|
67
|
+
data["dsa_compliance_note"] = "This account is subject to European DSA (Digital Services Act) requirements"
|
|
68
|
+
else:
|
|
69
|
+
data["dsa_required"] = False
|
|
70
|
+
data["dsa_compliance_note"] = "This account is not subject to European DSA requirements"
|
|
71
|
+
|
|
62
72
|
return json.dumps(data, indent=2)
|
|
@@ -73,7 +73,7 @@ async def get_adset_details(access_token: str = None, adset_id: str = None) -> s
|
|
|
73
73
|
endpoint = f"{adset_id}"
|
|
74
74
|
# Explicitly prioritize frequency_control_specs in the fields request
|
|
75
75
|
params = {
|
|
76
|
-
"fields": "id,name,campaign_id,status,frequency_control_specs{event,interval_days,max_frequency},daily_budget,lifetime_budget,targeting,bid_amount,bid_strategy,optimization_goal,billing_event,start_time,end_time,created_time,updated_time,attribution_spec,destination_type,promoted_object,pacing_type,budget_remaining"
|
|
76
|
+
"fields": "id,name,campaign_id,status,frequency_control_specs{event,interval_days,max_frequency},daily_budget,lifetime_budget,targeting,bid_amount,bid_strategy,optimization_goal,billing_event,start_time,end_time,created_time,updated_time,attribution_spec,destination_type,promoted_object,pacing_type,budget_remaining,dsa_beneficiary"
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
data = await make_api_request(endpoint, access_token, params)
|
|
@@ -103,6 +103,7 @@ async def create_adset(
|
|
|
103
103
|
bid_strategy: str = None,
|
|
104
104
|
start_time: str = None,
|
|
105
105
|
end_time: str = None,
|
|
106
|
+
dsa_beneficiary: str = None,
|
|
106
107
|
access_token: str = None
|
|
107
108
|
) -> str:
|
|
108
109
|
"""
|
|
@@ -123,6 +124,7 @@ async def create_adset(
|
|
|
123
124
|
bid_strategy: Bid strategy (e.g., 'LOWEST_COST', 'LOWEST_COST_WITH_BID_CAP')
|
|
124
125
|
start_time: Start time in ISO 8601 format (e.g., '2023-12-01T12:00:00-0800')
|
|
125
126
|
end_time: End time in ISO 8601 format
|
|
127
|
+
dsa_beneficiary: DSA beneficiary (person/organization benefiting from ads) for European compliance
|
|
126
128
|
access_token: Meta API access token (optional - will use cached token if not provided)
|
|
127
129
|
"""
|
|
128
130
|
# Check required parameters
|
|
@@ -181,16 +183,44 @@ async def create_adset(
|
|
|
181
183
|
if end_time:
|
|
182
184
|
params["end_time"] = end_time
|
|
183
185
|
|
|
186
|
+
# Add DSA beneficiary if provided
|
|
187
|
+
if dsa_beneficiary:
|
|
188
|
+
params["dsa_beneficiary"] = dsa_beneficiary
|
|
189
|
+
|
|
184
190
|
try:
|
|
185
191
|
data = await make_api_request(endpoint, access_token, params, method="POST")
|
|
186
192
|
return json.dumps(data, indent=2)
|
|
187
193
|
except Exception as e:
|
|
188
194
|
error_msg = str(e)
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
195
|
+
|
|
196
|
+
# Enhanced error handling for DSA beneficiary issues
|
|
197
|
+
if "permission" in error_msg.lower() or "insufficient" in error_msg.lower():
|
|
198
|
+
return json.dumps({
|
|
199
|
+
"error": "Insufficient permissions to set DSA beneficiary. Please ensure you have business_management permissions.",
|
|
200
|
+
"details": error_msg,
|
|
201
|
+
"params_sent": params,
|
|
202
|
+
"permission_required": True
|
|
203
|
+
}, indent=2)
|
|
204
|
+
elif "dsa_beneficiary" in error_msg.lower() and ("not supported" in error_msg.lower() or "parameter" in error_msg.lower()):
|
|
205
|
+
return json.dumps({
|
|
206
|
+
"error": "DSA beneficiary parameter not supported in this API version. Please set DSA beneficiary manually in Facebook Ads Manager.",
|
|
207
|
+
"details": error_msg,
|
|
208
|
+
"params_sent": params,
|
|
209
|
+
"manual_setup_required": True
|
|
210
|
+
}, indent=2)
|
|
211
|
+
elif "benefits from ads" in error_msg or "DSA beneficiary" in error_msg:
|
|
212
|
+
return json.dumps({
|
|
213
|
+
"error": "DSA beneficiary required for European compliance. Please provide the person or organization that benefits from ads in this ad set.",
|
|
214
|
+
"details": error_msg,
|
|
215
|
+
"params_sent": params,
|
|
216
|
+
"dsa_required": True
|
|
217
|
+
}, indent=2)
|
|
218
|
+
else:
|
|
219
|
+
return json.dumps({
|
|
220
|
+
"error": "Failed to create ad set",
|
|
221
|
+
"details": error_msg,
|
|
222
|
+
"params_sent": params
|
|
223
|
+
}, indent=2)
|
|
194
224
|
|
|
195
225
|
|
|
196
226
|
@mcp_server.tool()
|
|
@@ -33,7 +33,7 @@ async def get_insights(access_token: str = None, object_id: str = None,
|
|
|
33
33
|
|
|
34
34
|
endpoint = f"{object_id}/insights"
|
|
35
35
|
params = {
|
|
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",
|
|
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,action_values,conversions,unique_clicks,cost_per_action_type",
|
|
37
37
|
"level": level
|
|
38
38
|
}
|
|
39
39
|
|