meta-ads-mcp-python 1.0.79__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 +79 -0
- meta_ads_mcp/__main__.py +10 -0
- meta_ads_mcp/core/__init__.py +55 -0
- meta_ads_mcp/core/accounts.py +141 -0
- meta_ads_mcp/core/ads.py +2751 -0
- meta_ads_mcp/core/ads_library.py +74 -0
- meta_ads_mcp/core/adsets.py +666 -0
- meta_ads_mcp/core/api.py +431 -0
- meta_ads_mcp/core/auth.py +567 -0
- meta_ads_mcp/core/authentication.py +207 -0
- meta_ads_mcp/core/budget_schedules.py +70 -0
- meta_ads_mcp/core/callback_server.py +256 -0
- meta_ads_mcp/core/campaigns.py +379 -0
- meta_ads_mcp/core/duplication.py +523 -0
- meta_ads_mcp/core/http_auth_integration.py +307 -0
- meta_ads_mcp/core/insights.py +161 -0
- meta_ads_mcp/core/mcc.py +232 -0
- meta_ads_mcp/core/openai_deep_research.py +418 -0
- meta_ads_mcp/core/pipeboard_auth.py +510 -0
- meta_ads_mcp/core/reports.py +135 -0
- meta_ads_mcp/core/resources.py +46 -0
- meta_ads_mcp/core/server.py +391 -0
- meta_ads_mcp/core/targeting.py +542 -0
- meta_ads_mcp/core/utils.py +225 -0
- meta_ads_mcp/settings.py +33 -0
- meta_ads_mcp_python-1.0.79.dist-info/METADATA +187 -0
- meta_ads_mcp_python-1.0.79.dist-info/RECORD +29 -0
- meta_ads_mcp_python-1.0.79.dist-info/WHEEL +4 -0
- meta_ads_mcp_python-1.0.79.dist-info/entry_points.txt +3 -0
meta_ads_mcp/__init__.py
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Meta Ads MCP - Python Package
|
|
3
|
+
|
|
4
|
+
This package provides a Meta Ads MCP integration
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from meta_ads_mcp.core.server import main
|
|
8
|
+
|
|
9
|
+
__version__ = "1.0.79"
|
|
10
|
+
|
|
11
|
+
__all__ = [
|
|
12
|
+
'get_ad_accounts',
|
|
13
|
+
'get_account_info',
|
|
14
|
+
'get_campaigns',
|
|
15
|
+
'get_campaign_details',
|
|
16
|
+
'create_campaign',
|
|
17
|
+
'get_adsets',
|
|
18
|
+
'get_adset_details',
|
|
19
|
+
'update_adset',
|
|
20
|
+
'get_ads',
|
|
21
|
+
'get_ad_details',
|
|
22
|
+
'get_ad_creatives',
|
|
23
|
+
'get_ad_image',
|
|
24
|
+
'update_ad',
|
|
25
|
+
'get_insights',
|
|
26
|
+
# 'get_login_link' is conditionally exported via core.__all__
|
|
27
|
+
'login_cli',
|
|
28
|
+
'main',
|
|
29
|
+
'search_interests',
|
|
30
|
+
'get_interest_suggestions',
|
|
31
|
+
'estimate_audience_size',
|
|
32
|
+
'search_behaviors',
|
|
33
|
+
'search_demographics',
|
|
34
|
+
'search_geo_locations',
|
|
35
|
+
# MCC / Business Manager tools
|
|
36
|
+
'get_businesses',
|
|
37
|
+
'get_business_ad_accounts',
|
|
38
|
+
'find_client_account',
|
|
39
|
+
'get_business_details',
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
# Import key functions to make them available at package level
|
|
43
|
+
from .core import (
|
|
44
|
+
get_ad_accounts,
|
|
45
|
+
get_account_info,
|
|
46
|
+
get_campaigns,
|
|
47
|
+
get_campaign_details,
|
|
48
|
+
create_campaign,
|
|
49
|
+
get_adsets,
|
|
50
|
+
get_adset_details,
|
|
51
|
+
update_adset,
|
|
52
|
+
get_ads,
|
|
53
|
+
get_ad_details,
|
|
54
|
+
get_ad_creatives,
|
|
55
|
+
get_ad_image,
|
|
56
|
+
update_ad,
|
|
57
|
+
get_insights,
|
|
58
|
+
login_cli,
|
|
59
|
+
main,
|
|
60
|
+
search_interests,
|
|
61
|
+
get_interest_suggestions,
|
|
62
|
+
estimate_audience_size,
|
|
63
|
+
search_behaviors,
|
|
64
|
+
search_demographics,
|
|
65
|
+
search_geo_locations,
|
|
66
|
+
# MCC / Business Manager tools
|
|
67
|
+
get_businesses,
|
|
68
|
+
get_business_ad_accounts,
|
|
69
|
+
find_client_account,
|
|
70
|
+
get_business_details,
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
# Define a main function to be used as a package entry point
|
|
74
|
+
def entrypoint():
|
|
75
|
+
"""Main entry point for the package when invoked with uvx."""
|
|
76
|
+
return main()
|
|
77
|
+
|
|
78
|
+
# Re-export main for direct access
|
|
79
|
+
main = main
|
meta_ads_mcp/__main__.py
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"""Core functionality for Meta Ads API MCP package."""
|
|
2
|
+
|
|
3
|
+
from .server import mcp_server
|
|
4
|
+
from .accounts import get_ad_accounts, get_account_info
|
|
5
|
+
from .campaigns import get_campaigns, get_campaign_details
|
|
6
|
+
from .adsets import get_adsets, get_adset_details
|
|
7
|
+
from .ads import get_ads, get_ad_details, get_creative_details, get_ad_creatives, get_ad_image
|
|
8
|
+
from .insights import get_insights
|
|
9
|
+
from . import authentication # Import module to register conditional auth tools
|
|
10
|
+
from .server import login_cli, main
|
|
11
|
+
from .auth import login
|
|
12
|
+
from . import ads_library # Import module to register conditional tools
|
|
13
|
+
from .targeting import search_interests, get_interest_suggestions, estimate_audience_size, search_behaviors, search_demographics, search_geo_locations
|
|
14
|
+
from . import reports # Import module to register conditional tools
|
|
15
|
+
from . import duplication # Import module to register conditional duplication tools
|
|
16
|
+
from .openai_deep_research import search, fetch # OpenAI MCP Deep Research tools
|
|
17
|
+
from .mcc import ( # Business Manager (MCC) tools
|
|
18
|
+
get_businesses,
|
|
19
|
+
get_business_ad_accounts,
|
|
20
|
+
find_client_account,
|
|
21
|
+
get_business_details,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
__all__ = [
|
|
25
|
+
'mcp_server',
|
|
26
|
+
'get_ad_accounts',
|
|
27
|
+
'get_account_info',
|
|
28
|
+
'get_campaigns',
|
|
29
|
+
'get_campaign_details',
|
|
30
|
+
'get_adsets',
|
|
31
|
+
'get_adset_details',
|
|
32
|
+
'get_ads',
|
|
33
|
+
'get_ad_details',
|
|
34
|
+
'get_creative_details',
|
|
35
|
+
'get_ad_creatives',
|
|
36
|
+
'get_ad_image',
|
|
37
|
+
'get_insights',
|
|
38
|
+
# Note: 'get_login_link' is registered conditionally by the authentication module
|
|
39
|
+
'login_cli',
|
|
40
|
+
'login',
|
|
41
|
+
'main',
|
|
42
|
+
'search_interests',
|
|
43
|
+
'get_interest_suggestions',
|
|
44
|
+
'estimate_audience_size',
|
|
45
|
+
'search_behaviors',
|
|
46
|
+
'search_demographics',
|
|
47
|
+
'search_geo_locations',
|
|
48
|
+
'search', # OpenAI MCP Deep Research search tool
|
|
49
|
+
'fetch', # OpenAI MCP Deep Research fetch tool
|
|
50
|
+
# MCC / Business Manager tools
|
|
51
|
+
'get_businesses',
|
|
52
|
+
'get_business_ad_accounts',
|
|
53
|
+
'find_client_account',
|
|
54
|
+
'get_business_details',
|
|
55
|
+
]
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
"""Account-related functionality for Meta Ads API."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from typing import Optional, Dict, Any
|
|
5
|
+
from .api import meta_api_tool, make_api_request, ensure_act_prefix
|
|
6
|
+
from .server import mcp_server
|
|
7
|
+
|
|
8
|
+
# Currencies that have no sub-units (i.e., are not denominated in cents).
|
|
9
|
+
# Meta API returns amount_spent and balance as integers in the smallest currency
|
|
10
|
+
# unit, which is cents for most currencies but the base unit for these.
|
|
11
|
+
_ZERO_DECIMAL_CURRENCIES = {
|
|
12
|
+
"BIF", "CLP", "DJF", "GNF", "JPY", "KMF", "KRW", "MGA",
|
|
13
|
+
"PYG", "RWF", "UGX", "VND", "VUV", "XAF", "XOF", "XPF",
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _cents_to_currency(amount, currency: str) -> str:
|
|
18
|
+
"""Convert a Meta API monetary value (cents) to a currency-unit string.
|
|
19
|
+
|
|
20
|
+
Meta returns amount_spent and balance as integers representing the smallest
|
|
21
|
+
currency unit (cents for USD/EUR/GBP, base unit for zero-decimal currencies
|
|
22
|
+
like JPY). This converts to the human-readable decimal amount.
|
|
23
|
+
"""
|
|
24
|
+
try:
|
|
25
|
+
amount_int = int(amount)
|
|
26
|
+
except (TypeError, ValueError):
|
|
27
|
+
return str(amount)
|
|
28
|
+
if currency.upper() in _ZERO_DECIMAL_CURRENCIES:
|
|
29
|
+
return str(amount_int)
|
|
30
|
+
return f"{amount_int / 100:.2f}"
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _normalize_account_monetary_fields(account: dict) -> dict:
|
|
34
|
+
"""Convert amount_spent and balance from cents to currency units in-place."""
|
|
35
|
+
currency = account.get("currency", "USD")
|
|
36
|
+
for field in ("amount_spent", "balance"):
|
|
37
|
+
if field in account:
|
|
38
|
+
account[field] = _cents_to_currency(account[field], currency)
|
|
39
|
+
return account
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@mcp_server.tool()
|
|
43
|
+
@meta_api_tool
|
|
44
|
+
async def get_ad_accounts(access_token: Optional[str] = None, user_id: str = "me", limit: int = 200) -> str:
|
|
45
|
+
"""
|
|
46
|
+
Get ad accounts accessible by a user.
|
|
47
|
+
|
|
48
|
+
amount_spent and balance are returned in currency units (e.g. USD dollars),
|
|
49
|
+
not cents.
|
|
50
|
+
|
|
51
|
+
Args:
|
|
52
|
+
access_token: Meta API access token (optional - will use cached token if not provided)
|
|
53
|
+
user_id: Meta user ID or "me" for the current user
|
|
54
|
+
limit: Maximum number of accounts to return (default: 200)
|
|
55
|
+
"""
|
|
56
|
+
endpoint = f"{user_id}/adaccounts"
|
|
57
|
+
params = {
|
|
58
|
+
"fields": "id,name,account_id,account_status,amount_spent,balance,currency,age,business_city,business_country_code",
|
|
59
|
+
"limit": limit
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
data = await make_api_request(endpoint, access_token, params)
|
|
63
|
+
|
|
64
|
+
if "data" in data:
|
|
65
|
+
data["data"] = [_normalize_account_monetary_fields(acc) for acc in data["data"]]
|
|
66
|
+
|
|
67
|
+
return json.dumps(data, indent=2)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
@mcp_server.tool()
|
|
71
|
+
@meta_api_tool
|
|
72
|
+
async def get_account_info(account_id: str, access_token: Optional[str] = None) -> str:
|
|
73
|
+
"""
|
|
74
|
+
Get detailed information about a specific ad account.
|
|
75
|
+
|
|
76
|
+
Args:
|
|
77
|
+
account_id: Meta Ads account ID (format: act_XXXXXXXXX)
|
|
78
|
+
access_token: Meta API access token (optional - will use cached token if not provided)
|
|
79
|
+
"""
|
|
80
|
+
if not account_id:
|
|
81
|
+
return {
|
|
82
|
+
"error": {
|
|
83
|
+
"message": "Account ID is required",
|
|
84
|
+
"details": "Please specify an account_id parameter",
|
|
85
|
+
"example": "Use account_id='act_123456789' or account_id='123456789'"
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
account_id = ensure_act_prefix(account_id)
|
|
90
|
+
|
|
91
|
+
# Try to get the account info directly first
|
|
92
|
+
endpoint = f"{account_id}"
|
|
93
|
+
params = {
|
|
94
|
+
"fields": "id,name,account_id,account_status,amount_spent,balance,currency,age,business_city,business_country_code,timezone_name"
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
data = await make_api_request(endpoint, access_token, params)
|
|
98
|
+
|
|
99
|
+
# Check if the API request returned an error
|
|
100
|
+
if "error" in data:
|
|
101
|
+
# If access was denied, provide helpful error message with accessible accounts
|
|
102
|
+
if "access" in str(data.get("error", {})).lower() or "permission" in str(data.get("error", {})).lower():
|
|
103
|
+
# Get list of accessible accounts for helpful error message
|
|
104
|
+
accessible_endpoint = "me/adaccounts"
|
|
105
|
+
accessible_params = {
|
|
106
|
+
"fields": "id,name,account_id,account_status,amount_spent,balance,currency,age,business_city,business_country_code",
|
|
107
|
+
"limit": 50
|
|
108
|
+
}
|
|
109
|
+
accessible_accounts_data = await make_api_request(accessible_endpoint, access_token, accessible_params)
|
|
110
|
+
|
|
111
|
+
if "data" in accessible_accounts_data:
|
|
112
|
+
accessible_accounts = [
|
|
113
|
+
{"id": acc["id"], "name": acc["name"]}
|
|
114
|
+
for acc in accessible_accounts_data["data"][:10] # Show first 10
|
|
115
|
+
]
|
|
116
|
+
return {
|
|
117
|
+
"error": {
|
|
118
|
+
"message": f"Account {account_id} is not accessible to your user account",
|
|
119
|
+
"details": "This account either doesn't exist or you don't have permission to access it",
|
|
120
|
+
"accessible_accounts": accessible_accounts,
|
|
121
|
+
"total_accessible_accounts": len(accessible_accounts_data["data"]),
|
|
122
|
+
"suggestion": "Try using one of the accessible account IDs listed above"
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
# Return the original error for non-permission related issues
|
|
127
|
+
return data
|
|
128
|
+
|
|
129
|
+
_normalize_account_monetary_fields(data)
|
|
130
|
+
|
|
131
|
+
# Add DSA requirement detection
|
|
132
|
+
if "business_country_code" in data:
|
|
133
|
+
european_countries = ["DE", "FR", "IT", "ES", "NL", "BE", "AT", "IE", "DK", "SE", "FI", "NO"]
|
|
134
|
+
if data["business_country_code"] in european_countries:
|
|
135
|
+
data["dsa_required"] = True
|
|
136
|
+
data["dsa_compliance_note"] = "This account is subject to European DSA (Digital Services Act) requirements"
|
|
137
|
+
else:
|
|
138
|
+
data["dsa_required"] = False
|
|
139
|
+
data["dsa_compliance_note"] = "This account is not subject to European DSA requirements"
|
|
140
|
+
|
|
141
|
+
return data
|