meta-ads-mcp 0.4.1__py3-none-any.whl → 0.4.2__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/core/duplication.py +38 -6
- meta_ads_mcp/core/http_auth_integration.py +66 -7
- {meta_ads_mcp-0.4.1.dist-info → meta_ads_mcp-0.4.2.dist-info}/METADATA +1 -1
- {meta_ads_mcp-0.4.1.dist-info → meta_ads_mcp-0.4.2.dist-info}/RECORD +8 -8
- {meta_ads_mcp-0.4.1.dist-info → meta_ads_mcp-0.4.2.dist-info}/WHEEL +0 -0
- {meta_ads_mcp-0.4.1.dist-info → meta_ads_mcp-0.4.2.dist-info}/entry_points.txt +0 -0
- {meta_ads_mcp-0.4.1.dist-info → meta_ads_mcp-0.4.2.dist-info}/licenses/LICENSE +0 -0
meta_ads_mcp/__init__.py
CHANGED
meta_ads_mcp/core/duplication.py
CHANGED
|
@@ -6,6 +6,8 @@ import httpx
|
|
|
6
6
|
from typing import Optional, Dict, Any, List, Union
|
|
7
7
|
from .server import mcp_server
|
|
8
8
|
from .api import meta_api_tool
|
|
9
|
+
from .auth import get_current_access_token
|
|
10
|
+
from .http_auth_integration import FastMCPAuthIntegration
|
|
9
11
|
|
|
10
12
|
|
|
11
13
|
# Only register the duplication functions if the environment variable is set
|
|
@@ -178,21 +180,50 @@ if ENABLE_DUPLICATION:
|
|
|
178
180
|
|
|
179
181
|
async def _forward_duplication_request(resource_type: str, resource_id: str, access_token: str, options: Dict[str, Any]) -> str:
|
|
180
182
|
"""
|
|
181
|
-
Forward duplication request to the cloud-hosted MCP API.
|
|
183
|
+
Forward duplication request to the cloud-hosted MCP API using dual-header authentication.
|
|
184
|
+
|
|
185
|
+
This implements the dual-header authentication pattern for MCP server callbacks:
|
|
186
|
+
- Authorization: Bearer <facebook_token> - Facebook access token for Meta API calls
|
|
187
|
+
- X-Pipeboard-Token: <pipeboard_token> - Pipeboard API token for authentication
|
|
182
188
|
|
|
183
189
|
Args:
|
|
184
190
|
resource_type: Type of resource to duplicate (campaign, adset, ad, creative)
|
|
185
191
|
resource_id: ID of the resource to duplicate
|
|
186
|
-
access_token: Meta API access token
|
|
192
|
+
access_token: Meta API access token (optional, will use context if not provided)
|
|
187
193
|
options: Duplication options
|
|
188
194
|
"""
|
|
189
195
|
try:
|
|
190
|
-
|
|
196
|
+
# Get tokens from the request context that were set by the HTTP auth middleware
|
|
197
|
+
# In the dual-header authentication pattern:
|
|
198
|
+
# - Pipeboard token comes from X-Pipeboard-Token header (for authentication)
|
|
199
|
+
# - Facebook token comes from Authorization header (for Meta API calls)
|
|
200
|
+
|
|
201
|
+
# Get tokens from context set by AuthInjectionMiddleware
|
|
202
|
+
pipeboard_token = FastMCPAuthIntegration.get_pipeboard_token()
|
|
203
|
+
facebook_token = FastMCPAuthIntegration.get_auth_token()
|
|
204
|
+
|
|
205
|
+
# Use provided access_token parameter if no Facebook token found in context
|
|
206
|
+
if not facebook_token:
|
|
207
|
+
facebook_token = access_token if access_token else await get_current_access_token()
|
|
208
|
+
|
|
209
|
+
# Validate we have both required tokens
|
|
210
|
+
if not pipeboard_token:
|
|
211
|
+
return json.dumps({
|
|
212
|
+
"error": "authentication_required",
|
|
213
|
+
"message": "Pipeboard API token not found",
|
|
214
|
+
"details": {
|
|
215
|
+
"required": "Valid Pipeboard token via X-Pipeboard-Token header",
|
|
216
|
+
"received_headers": "Check that the MCP server is forwarding the X-Pipeboard-Token header"
|
|
217
|
+
}
|
|
218
|
+
}, indent=2)
|
|
219
|
+
|
|
220
|
+
if not facebook_token:
|
|
191
221
|
return json.dumps({
|
|
192
222
|
"error": "authentication_required",
|
|
193
223
|
"message": "Meta Ads access token not found",
|
|
194
224
|
"details": {
|
|
195
|
-
"required": "Valid access token from authenticated session"
|
|
225
|
+
"required": "Valid Meta access token from authenticated session",
|
|
226
|
+
"check": "Ensure Facebook account is connected and token is valid"
|
|
196
227
|
}
|
|
197
228
|
}, indent=2)
|
|
198
229
|
|
|
@@ -200,9 +231,10 @@ async def _forward_duplication_request(resource_type: str, resource_id: str, acc
|
|
|
200
231
|
base_url = "https://mcp.pipeboard.co"
|
|
201
232
|
endpoint = f"{base_url}/api/meta/duplicate/{resource_type}/{resource_id}"
|
|
202
233
|
|
|
203
|
-
# Prepare the
|
|
234
|
+
# Prepare the dual-header authentication as per API documentation
|
|
204
235
|
headers = {
|
|
205
|
-
"Authorization": f"Bearer {
|
|
236
|
+
"Authorization": f"Bearer {facebook_token}", # Facebook token for Meta API
|
|
237
|
+
"X-Pipeboard-Token": pipeboard_token, # Pipeboard token for auth
|
|
206
238
|
"Content-Type": "application/json",
|
|
207
239
|
"User-Agent": "meta-ads-mcp/1.0"
|
|
208
240
|
}
|
|
@@ -13,6 +13,7 @@ import json
|
|
|
13
13
|
|
|
14
14
|
# Use context variables instead of thread-local storage for better async support
|
|
15
15
|
_auth_token: contextvars.ContextVar[Optional[str]] = contextvars.ContextVar('auth_token', default=None)
|
|
16
|
+
_pipeboard_token: contextvars.ContextVar[Optional[str]] = contextvars.ContextVar('pipeboard_token', default=None)
|
|
16
17
|
|
|
17
18
|
class FastMCPAuthIntegration:
|
|
18
19
|
"""Direct integration with FastMCP for HTTP authentication"""
|
|
@@ -35,11 +36,34 @@ class FastMCPAuthIntegration:
|
|
|
35
36
|
"""
|
|
36
37
|
return _auth_token.get(None)
|
|
37
38
|
|
|
39
|
+
@staticmethod
|
|
40
|
+
def set_pipeboard_token(token: str) -> None:
|
|
41
|
+
"""Set Pipeboard token for the current context
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
token: Pipeboard API token to use for this request
|
|
45
|
+
"""
|
|
46
|
+
_pipeboard_token.set(token)
|
|
47
|
+
|
|
48
|
+
@staticmethod
|
|
49
|
+
def get_pipeboard_token() -> Optional[str]:
|
|
50
|
+
"""Get Pipeboard token for the current context
|
|
51
|
+
|
|
52
|
+
Returns:
|
|
53
|
+
Pipeboard token if set, None otherwise
|
|
54
|
+
"""
|
|
55
|
+
return _pipeboard_token.get(None)
|
|
56
|
+
|
|
38
57
|
@staticmethod
|
|
39
58
|
def clear_auth_token() -> None:
|
|
40
59
|
"""Clear authentication token for the current context"""
|
|
41
60
|
_auth_token.set(None)
|
|
42
61
|
|
|
62
|
+
@staticmethod
|
|
63
|
+
def clear_pipeboard_token() -> None:
|
|
64
|
+
"""Clear Pipeboard token for the current context"""
|
|
65
|
+
_pipeboard_token.set(None)
|
|
66
|
+
|
|
43
67
|
@staticmethod
|
|
44
68
|
def extract_token_from_headers(headers: dict) -> Optional[str]:
|
|
45
69
|
"""Extract token from HTTP headers
|
|
@@ -69,6 +93,30 @@ class FastMCPAuthIntegration:
|
|
|
69
93
|
return pipeboard_token
|
|
70
94
|
|
|
71
95
|
return None
|
|
96
|
+
|
|
97
|
+
@staticmethod
|
|
98
|
+
def extract_pipeboard_token_from_headers(headers: dict) -> Optional[str]:
|
|
99
|
+
"""Extract Pipeboard token from HTTP headers
|
|
100
|
+
|
|
101
|
+
Args:
|
|
102
|
+
headers: HTTP request headers
|
|
103
|
+
|
|
104
|
+
Returns:
|
|
105
|
+
Pipeboard token if found, None otherwise
|
|
106
|
+
"""
|
|
107
|
+
# Check for Pipeboard token in X-Pipeboard-Token header (duplication API pattern)
|
|
108
|
+
pipeboard_token = headers.get('X-Pipeboard-Token') or headers.get('x-pipeboard-token')
|
|
109
|
+
if pipeboard_token:
|
|
110
|
+
logger.debug("Found Pipeboard token in X-Pipeboard-Token header")
|
|
111
|
+
return pipeboard_token
|
|
112
|
+
|
|
113
|
+
# Check for legacy Pipeboard token header
|
|
114
|
+
legacy_token = headers.get('X-PIPEBOARD-API-TOKEN') or headers.get('x-pipeboard-api-token')
|
|
115
|
+
if legacy_token:
|
|
116
|
+
logger.debug("Found Pipeboard token in legacy X-PIPEBOARD-API-TOKEN header")
|
|
117
|
+
return legacy_token
|
|
118
|
+
|
|
119
|
+
return None
|
|
72
120
|
|
|
73
121
|
def patch_fastmcp_server(mcp_server):
|
|
74
122
|
"""Patch FastMCP server to inject authentication from HTTP headers
|
|
@@ -203,21 +251,32 @@ class AuthInjectionMiddleware(BaseHTTPMiddleware):
|
|
|
203
251
|
logger.debug(f"HTTP Auth Middleware: Processing request to {request.url.path}")
|
|
204
252
|
logger.debug(f"HTTP Auth Middleware: Request headers: {list(request.headers.keys())}")
|
|
205
253
|
|
|
206
|
-
|
|
254
|
+
# Extract both types of tokens for dual-header authentication
|
|
255
|
+
auth_token = FastMCPAuthIntegration.extract_token_from_headers(dict(request.headers))
|
|
256
|
+
pipeboard_token = FastMCPAuthIntegration.extract_pipeboard_token_from_headers(dict(request.headers))
|
|
207
257
|
|
|
208
|
-
if
|
|
209
|
-
logger.debug(f"HTTP Auth Middleware: Extracted token: {
|
|
258
|
+
if auth_token:
|
|
259
|
+
logger.debug(f"HTTP Auth Middleware: Extracted auth token: {auth_token[:10]}...")
|
|
210
260
|
logger.debug("Injecting auth token into request context")
|
|
211
|
-
FastMCPAuthIntegration.set_auth_token(
|
|
212
|
-
|
|
213
|
-
|
|
261
|
+
FastMCPAuthIntegration.set_auth_token(auth_token)
|
|
262
|
+
|
|
263
|
+
if pipeboard_token:
|
|
264
|
+
logger.debug(f"HTTP Auth Middleware: Extracted Pipeboard token: {pipeboard_token[:10]}...")
|
|
265
|
+
logger.debug("Injecting Pipeboard token into request context")
|
|
266
|
+
FastMCPAuthIntegration.set_pipeboard_token(pipeboard_token)
|
|
267
|
+
|
|
268
|
+
if not auth_token and not pipeboard_token:
|
|
269
|
+
logger.warning("HTTP Auth Middleware: No authentication tokens found in headers")
|
|
214
270
|
|
|
215
271
|
try:
|
|
216
272
|
response = await call_next(request)
|
|
217
273
|
return response
|
|
218
274
|
finally:
|
|
219
|
-
|
|
275
|
+
# Clear tokens that were set for this request
|
|
276
|
+
if auth_token:
|
|
220
277
|
FastMCPAuthIntegration.clear_auth_token()
|
|
278
|
+
if pipeboard_token:
|
|
279
|
+
FastMCPAuthIntegration.clear_pipeboard_token()
|
|
221
280
|
|
|
222
281
|
def setup_starlette_middleware(app):
|
|
223
282
|
"""Add AuthInjectionMiddleware to the Starlette app if not already present.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: meta-ads-mcp
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.2
|
|
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
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
meta_ads_mcp/__init__.py,sha256=
|
|
1
|
+
meta_ads_mcp/__init__.py,sha256=r7i4rteDOpqUZK4ZZkN5oihxeyUjOIAx-0xIyMqwPkU,1182
|
|
2
2
|
meta_ads_mcp/__main__.py,sha256=XaQt3iXftG_7f0Zu7Wop9SeFgrD2WBn0EQOaPMc27d8,207
|
|
3
3
|
meta_ads_mcp/core/__init__.py,sha256=XVJjMOfdgnqxy3k8vCn2PCf7za8fMk4BdgJGiSFCVZY,1209
|
|
4
4
|
meta_ads_mcp/core/accounts.py,sha256=Nmp7lPxO9wmq25jWV7_H0LIqnEbBhpCVBlLGW2HUaq0,2277
|
|
@@ -11,16 +11,16 @@ meta_ads_mcp/core/authentication.py,sha256=4CH2Fe3w7Al7YE2wgoa0DW5qOXTp_5Lsa4T6_
|
|
|
11
11
|
meta_ads_mcp/core/budget_schedules.py,sha256=UxseExsvKAiPwfDCY9aycT4kys4xqeNytyq-yyDOxrs,2901
|
|
12
12
|
meta_ads_mcp/core/callback_server.py,sha256=wNuxmj7YTFeSdVGi_iJ9vberNy3VdzBIP0uSsqn7g5Q,43888
|
|
13
13
|
meta_ads_mcp/core/campaigns.py,sha256=Fd477GsD1Gx08Ve0uXUCvr4fC-xQCeVHPBwRVaeRQKk,10965
|
|
14
|
-
meta_ads_mcp/core/duplication.py,sha256=
|
|
15
|
-
meta_ads_mcp/core/http_auth_integration.py,sha256=
|
|
14
|
+
meta_ads_mcp/core/duplication.py,sha256=UUmTDFx9o5ZsPQG2Rb9c4ZyuKUVN3FfTjebfTIHHdo4,18984
|
|
15
|
+
meta_ads_mcp/core/http_auth_integration.py,sha256=lGpKhfzJcyWugBcYEvypY-qnlt-3UDBLqh7xAUH0DGw,12473
|
|
16
16
|
meta_ads_mcp/core/insights.py,sha256=U7KYdWQpGcdykE1WUtdJdYR3VTwKrXUzIzCREwWbf48,2599
|
|
17
17
|
meta_ads_mcp/core/pipeboard_auth.py,sha256=VvbxEB8ZOhnMccLU7HI1HgaPWHCl5NGrzZCm-zzHze4,22798
|
|
18
18
|
meta_ads_mcp/core/reports.py,sha256=Dv3hfsPOR7IZ9WrYrKd_6SNgZl-USIphg7knva3UYAw,5747
|
|
19
19
|
meta_ads_mcp/core/resources.py,sha256=-zIIfZulpo76vcKv6jhAlQq91cR2SZ3cjYZt3ek3x0w,1236
|
|
20
20
|
meta_ads_mcp/core/server.py,sha256=mmhtcyB7h1aO6jK4njLztPdAebPDmc3mhA7DksR1nlY,17583
|
|
21
21
|
meta_ads_mcp/core/utils.py,sha256=DsizDYuJnWUpkbShV1y5Qe8t47Qf59aPZ6O9v0hzdkY,6705
|
|
22
|
-
meta_ads_mcp-0.4.
|
|
23
|
-
meta_ads_mcp-0.4.
|
|
24
|
-
meta_ads_mcp-0.4.
|
|
25
|
-
meta_ads_mcp-0.4.
|
|
26
|
-
meta_ads_mcp-0.4.
|
|
22
|
+
meta_ads_mcp-0.4.2.dist-info/METADATA,sha256=M2VY7kNALGcXOYYjvwkxbmp3Ced9m3JO_hzAkjTxEQU,17239
|
|
23
|
+
meta_ads_mcp-0.4.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
24
|
+
meta_ads_mcp-0.4.2.dist-info/entry_points.txt,sha256=Dv2RkoBjRJBqj6CyhwqGIiwPCD-SCL1-7B9-zmVRuv0,57
|
|
25
|
+
meta_ads_mcp-0.4.2.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
26
|
+
meta_ads_mcp-0.4.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|