meta-ads-mcp 0.7.0__tar.gz → 0.7.2__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.0 → meta_ads_mcp-0.7.2}/PKG-INFO +1 -1
- {meta_ads_mcp-0.7.0 → meta_ads_mcp-0.7.2}/meta_ads_mcp/__init__.py +1 -1
- meta_ads_mcp-0.7.2/meta_ads_mcp/core/authentication.py +198 -0
- {meta_ads_mcp-0.7.0 → meta_ads_mcp-0.7.2}/pyproject.toml +1 -1
- meta_ads_mcp-0.7.0/meta_ads_mcp/core/authentication.py +0 -135
- {meta_ads_mcp-0.7.0 → meta_ads_mcp-0.7.2}/.github/workflows/publish.yml +0 -0
- {meta_ads_mcp-0.7.0 → meta_ads_mcp-0.7.2}/.github/workflows/test.yml +0 -0
- {meta_ads_mcp-0.7.0 → meta_ads_mcp-0.7.2}/.gitignore +0 -0
- {meta_ads_mcp-0.7.0 → meta_ads_mcp-0.7.2}/CUSTOM_META_APP.md +0 -0
- {meta_ads_mcp-0.7.0 → meta_ads_mcp-0.7.2}/Dockerfile +0 -0
- {meta_ads_mcp-0.7.0 → meta_ads_mcp-0.7.2}/LICENSE +0 -0
- {meta_ads_mcp-0.7.0 → meta_ads_mcp-0.7.2}/LOCAL_INSTALLATION.md +0 -0
- {meta_ads_mcp-0.7.0 → meta_ads_mcp-0.7.2}/META_API_NOTES.md +0 -0
- {meta_ads_mcp-0.7.0 → meta_ads_mcp-0.7.2}/README.md +0 -0
- {meta_ads_mcp-0.7.0 → meta_ads_mcp-0.7.2}/RELEASE.md +0 -0
- {meta_ads_mcp-0.7.0 → meta_ads_mcp-0.7.2}/STREAMABLE_HTTP_SETUP.md +0 -0
- {meta_ads_mcp-0.7.0 → meta_ads_mcp-0.7.2}/examples/README.md +0 -0
- {meta_ads_mcp-0.7.0 → meta_ads_mcp-0.7.2}/examples/example_http_client.py +0 -0
- {meta_ads_mcp-0.7.0 → meta_ads_mcp-0.7.2}/future_improvements.md +0 -0
- {meta_ads_mcp-0.7.0 → meta_ads_mcp-0.7.2}/images/meta-ads-example.png +0 -0
- {meta_ads_mcp-0.7.0 → meta_ads_mcp-0.7.2}/meta_ads_auth.sh +0 -0
- {meta_ads_mcp-0.7.0 → meta_ads_mcp-0.7.2}/meta_ads_mcp/__main__.py +0 -0
- {meta_ads_mcp-0.7.0 → meta_ads_mcp-0.7.2}/meta_ads_mcp/core/__init__.py +0 -0
- {meta_ads_mcp-0.7.0 → meta_ads_mcp-0.7.2}/meta_ads_mcp/core/accounts.py +0 -0
- {meta_ads_mcp-0.7.0 → meta_ads_mcp-0.7.2}/meta_ads_mcp/core/ads.py +0 -0
- {meta_ads_mcp-0.7.0 → meta_ads_mcp-0.7.2}/meta_ads_mcp/core/ads_library.py +0 -0
- {meta_ads_mcp-0.7.0 → meta_ads_mcp-0.7.2}/meta_ads_mcp/core/adsets.py +0 -0
- {meta_ads_mcp-0.7.0 → meta_ads_mcp-0.7.2}/meta_ads_mcp/core/api.py +0 -0
- {meta_ads_mcp-0.7.0 → meta_ads_mcp-0.7.2}/meta_ads_mcp/core/auth.py +0 -0
- {meta_ads_mcp-0.7.0 → meta_ads_mcp-0.7.2}/meta_ads_mcp/core/budget_schedules.py +0 -0
- {meta_ads_mcp-0.7.0 → meta_ads_mcp-0.7.2}/meta_ads_mcp/core/callback_server.py +0 -0
- {meta_ads_mcp-0.7.0 → meta_ads_mcp-0.7.2}/meta_ads_mcp/core/campaigns.py +0 -0
- {meta_ads_mcp-0.7.0 → meta_ads_mcp-0.7.2}/meta_ads_mcp/core/duplication.py +0 -0
- {meta_ads_mcp-0.7.0 → meta_ads_mcp-0.7.2}/meta_ads_mcp/core/http_auth_integration.py +0 -0
- {meta_ads_mcp-0.7.0 → meta_ads_mcp-0.7.2}/meta_ads_mcp/core/insights.py +0 -0
- {meta_ads_mcp-0.7.0 → meta_ads_mcp-0.7.2}/meta_ads_mcp/core/openai_deep_research.py +0 -0
- {meta_ads_mcp-0.7.0 → meta_ads_mcp-0.7.2}/meta_ads_mcp/core/pipeboard_auth.py +0 -0
- {meta_ads_mcp-0.7.0 → meta_ads_mcp-0.7.2}/meta_ads_mcp/core/reports.py +0 -0
- {meta_ads_mcp-0.7.0 → meta_ads_mcp-0.7.2}/meta_ads_mcp/core/resources.py +0 -0
- {meta_ads_mcp-0.7.0 → meta_ads_mcp-0.7.2}/meta_ads_mcp/core/server.py +0 -0
- {meta_ads_mcp-0.7.0 → meta_ads_mcp-0.7.2}/meta_ads_mcp/core/targeting.py +0 -0
- {meta_ads_mcp-0.7.0 → meta_ads_mcp-0.7.2}/meta_ads_mcp/core/utils.py +0 -0
- {meta_ads_mcp-0.7.0 → meta_ads_mcp-0.7.2}/requirements.txt +0 -0
- {meta_ads_mcp-0.7.0 → meta_ads_mcp-0.7.2}/setup.py +0 -0
- {meta_ads_mcp-0.7.0 → meta_ads_mcp-0.7.2}/smithery.yaml +0 -0
- {meta_ads_mcp-0.7.0 → meta_ads_mcp-0.7.2}/tests/README.md +0 -0
- {meta_ads_mcp-0.7.0 → meta_ads_mcp-0.7.2}/tests/README_REGRESSION_TESTS.md +0 -0
- {meta_ads_mcp-0.7.0 → meta_ads_mcp-0.7.2}/tests/__init__.py +0 -0
- {meta_ads_mcp-0.7.0 → meta_ads_mcp-0.7.2}/tests/conftest.py +0 -0
- {meta_ads_mcp-0.7.0 → meta_ads_mcp-0.7.2}/tests/test_account_search.py +0 -0
- {meta_ads_mcp-0.7.0 → meta_ads_mcp-0.7.2}/tests/test_duplication.py +0 -0
- {meta_ads_mcp-0.7.0 → meta_ads_mcp-0.7.2}/tests/test_duplication_regression.py +0 -0
- {meta_ads_mcp-0.7.0 → meta_ads_mcp-0.7.2}/tests/test_get_ad_creatives_fix.py +0 -0
- {meta_ads_mcp-0.7.0 → meta_ads_mcp-0.7.2}/tests/test_get_ad_image_regression.py +0 -0
- {meta_ads_mcp-0.7.0 → meta_ads_mcp-0.7.2}/tests/test_http_transport.py +0 -0
- {meta_ads_mcp-0.7.0 → meta_ads_mcp-0.7.2}/tests/test_integration_openai_mcp.py +0 -0
- {meta_ads_mcp-0.7.0 → meta_ads_mcp-0.7.2}/tests/test_openai.py +0 -0
- {meta_ads_mcp-0.7.0 → meta_ads_mcp-0.7.2}/tests/test_openai_mcp_deep_research.py +0 -0
- {meta_ads_mcp-0.7.0 → meta_ads_mcp-0.7.2}/tests/test_targeting.py +0 -0
- {meta_ads_mcp-0.7.0 → meta_ads_mcp-0.7.2}/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.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
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
"""Authentication-specific functionality for Meta Ads API.
|
|
2
|
+
|
|
3
|
+
The Meta Ads MCP server supports three authentication modes:
|
|
4
|
+
|
|
5
|
+
1. **Development/Local Mode** (default)
|
|
6
|
+
- Uses local callback server on localhost:8080+ for OAuth redirect
|
|
7
|
+
- Requires META_ADS_DISABLE_CALLBACK_SERVER to NOT be set
|
|
8
|
+
- Best for local development and testing
|
|
9
|
+
|
|
10
|
+
2. **Production with API Token**
|
|
11
|
+
- Uses PIPEBOARD_API_TOKEN for server-to-server authentication
|
|
12
|
+
- Bypasses OAuth flow entirely
|
|
13
|
+
- Best for server deployments with pre-configured tokens
|
|
14
|
+
|
|
15
|
+
3. **Production OAuth Flow** (NEW)
|
|
16
|
+
- Uses Pipeboard OAuth endpoints for dynamic client registration
|
|
17
|
+
- Triggered when META_ADS_DISABLE_CALLBACK_SERVER is set but no PIPEBOARD_API_TOKEN
|
|
18
|
+
- Supports MCP clients that implement OAuth 2.0 discovery
|
|
19
|
+
|
|
20
|
+
Environment Variables:
|
|
21
|
+
- PIPEBOARD_API_TOKEN: Enables mode 2 (token-based auth)
|
|
22
|
+
- META_ADS_DISABLE_CALLBACK_SERVER: Disables local server, enables mode 3
|
|
23
|
+
- META_ACCESS_TOKEN: Direct Meta token (fallback)
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
import json
|
|
27
|
+
import asyncio
|
|
28
|
+
import os
|
|
29
|
+
from .api import meta_api_tool
|
|
30
|
+
from .auth import start_callback_server, shutdown_callback_server, auth_manager, get_current_access_token
|
|
31
|
+
from .server import mcp_server
|
|
32
|
+
from .utils import logger, META_APP_SECRET
|
|
33
|
+
from .pipeboard_auth import pipeboard_auth_manager
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@mcp_server.tool()
|
|
37
|
+
async def get_login_link(access_token: str = None) -> str:
|
|
38
|
+
"""
|
|
39
|
+
Get a clickable login link for Meta Ads authentication.
|
|
40
|
+
|
|
41
|
+
NOTE: This method should only be used if you're using your own Facebook app.
|
|
42
|
+
If using Pipeboard authentication (recommended), set the PIPEBOARD_API_TOKEN
|
|
43
|
+
environment variable instead (token obtainable via https://pipeboard.co).
|
|
44
|
+
|
|
45
|
+
Args:
|
|
46
|
+
access_token: Meta API access token (optional - will use cached token if not provided)
|
|
47
|
+
|
|
48
|
+
Returns:
|
|
49
|
+
A clickable resource link for Meta authentication
|
|
50
|
+
"""
|
|
51
|
+
# Check if we're using pipeboard authentication
|
|
52
|
+
using_pipeboard = bool(os.environ.get("PIPEBOARD_API_TOKEN", ""))
|
|
53
|
+
callback_server_disabled = bool(os.environ.get("META_ADS_DISABLE_CALLBACK_SERVER", ""))
|
|
54
|
+
|
|
55
|
+
if using_pipeboard:
|
|
56
|
+
# Pipeboard token-based authentication
|
|
57
|
+
try:
|
|
58
|
+
logger.info("Using Pipeboard token-based authentication")
|
|
59
|
+
|
|
60
|
+
# If an access token was provided, this is likely a test - return success
|
|
61
|
+
if access_token:
|
|
62
|
+
return json.dumps({
|
|
63
|
+
"message": "✅ Authentication Token Provided",
|
|
64
|
+
"status": "Using provided access token for authentication",
|
|
65
|
+
"token_info": f"Token preview: {access_token[:10]}...",
|
|
66
|
+
"authentication_method": "manual_token",
|
|
67
|
+
"ready_to_use": "You can now use all Meta Ads MCP tools and commands."
|
|
68
|
+
}, indent=2)
|
|
69
|
+
|
|
70
|
+
# Check if Pipeboard token is working
|
|
71
|
+
token = pipeboard_auth_manager.get_access_token()
|
|
72
|
+
if token:
|
|
73
|
+
return json.dumps({
|
|
74
|
+
"message": "✅ Already Authenticated",
|
|
75
|
+
"status": "You're successfully authenticated with Meta Ads via Pipeboard!",
|
|
76
|
+
"token_info": f"Token preview: {token[:10]}...",
|
|
77
|
+
"authentication_method": "pipeboard_token",
|
|
78
|
+
"ready_to_use": "You can now use all Meta Ads MCP tools and commands."
|
|
79
|
+
}, indent=2)
|
|
80
|
+
|
|
81
|
+
# Start Pipeboard auth flow
|
|
82
|
+
auth_data = pipeboard_auth_manager.initiate_auth_flow()
|
|
83
|
+
login_url = auth_data.get('loginUrl')
|
|
84
|
+
|
|
85
|
+
if login_url:
|
|
86
|
+
return json.dumps({
|
|
87
|
+
"message": "🔗 Click to Authenticate",
|
|
88
|
+
"login_url": login_url,
|
|
89
|
+
"markdown_link": f"[🚀 Authenticate with Meta Ads]({login_url})",
|
|
90
|
+
"instructions": "Click the link above to complete authentication with Meta Ads.",
|
|
91
|
+
"authentication_method": "pipeboard_oauth",
|
|
92
|
+
"what_happens_next": "After clicking, you'll be redirected to Meta's authentication page. Once completed, your token will be automatically saved.",
|
|
93
|
+
"token_duration": "Your token will be valid for approximately 60 days."
|
|
94
|
+
}, indent=2)
|
|
95
|
+
else:
|
|
96
|
+
return json.dumps({
|
|
97
|
+
"message": "❌ Authentication Error",
|
|
98
|
+
"error": "Could not generate authentication URL from Pipeboard",
|
|
99
|
+
"troubleshooting": [
|
|
100
|
+
"Check that your PIPEBOARD_API_TOKEN is valid",
|
|
101
|
+
"Ensure the Pipeboard service is accessible",
|
|
102
|
+
"Try again in a few moments"
|
|
103
|
+
],
|
|
104
|
+
"authentication_method": "pipeboard_oauth_failed"
|
|
105
|
+
}, indent=2)
|
|
106
|
+
|
|
107
|
+
except Exception as e:
|
|
108
|
+
logger.error(f"Error initiating Pipeboard auth flow: {e}")
|
|
109
|
+
return json.dumps({
|
|
110
|
+
"message": "❌ Pipeboard Authentication Error",
|
|
111
|
+
"error": f"Failed to initiate Pipeboard authentication: {str(e)}",
|
|
112
|
+
"troubleshooting": [
|
|
113
|
+
"✅ Check that PIPEBOARD_API_TOKEN environment variable is set correctly",
|
|
114
|
+
"🌐 Verify that pipeboard.co is accessible from your network",
|
|
115
|
+
"🔄 Try refreshing your Pipeboard API token",
|
|
116
|
+
"⏰ Wait a moment and try again"
|
|
117
|
+
],
|
|
118
|
+
"get_help": "Contact support if the issue persists",
|
|
119
|
+
"authentication_method": "pipeboard_error"
|
|
120
|
+
}, indent=2)
|
|
121
|
+
elif callback_server_disabled:
|
|
122
|
+
# Production OAuth flow - use Pipeboard OAuth endpoints directly
|
|
123
|
+
logger.info("Production OAuth flow - using Pipeboard OAuth endpoints")
|
|
124
|
+
|
|
125
|
+
return json.dumps({
|
|
126
|
+
"message": "🔐 Authentication Required",
|
|
127
|
+
"instructions": "Please sign in to your Pipeboard account to authenticate with Meta Ads.",
|
|
128
|
+
"sign_in_url": "https://pipeboard.co/auth/signin",
|
|
129
|
+
"markdown_link": "[🚀 Sign in to Pipeboard](https://pipeboard.co/auth/signin)",
|
|
130
|
+
"what_to_do": "Click the link above to sign in to your Pipeboard account and complete authentication.",
|
|
131
|
+
"authentication_method": "production_oauth"
|
|
132
|
+
}, indent=2)
|
|
133
|
+
else:
|
|
134
|
+
# Original Meta authentication flow (development/local)
|
|
135
|
+
# Check if we have a cached token
|
|
136
|
+
cached_token = auth_manager.get_access_token()
|
|
137
|
+
token_status = "No token" if not cached_token else "Valid token"
|
|
138
|
+
|
|
139
|
+
# If we already have a valid token and none was provided, just return success
|
|
140
|
+
if cached_token and not access_token:
|
|
141
|
+
logger.info("get_login_link called with existing valid token")
|
|
142
|
+
return json.dumps({
|
|
143
|
+
"message": "✅ Already Authenticated",
|
|
144
|
+
"status": "You're successfully authenticated with Meta Ads!",
|
|
145
|
+
"token_info": f"Token preview: {cached_token[:10]}...",
|
|
146
|
+
"created_at": auth_manager.token_info.created_at if hasattr(auth_manager, "token_info") else None,
|
|
147
|
+
"expires_in": auth_manager.token_info.expires_in if hasattr(auth_manager, "token_info") else None,
|
|
148
|
+
"authentication_method": "meta_oauth",
|
|
149
|
+
"ready_to_use": "You can now use all Meta Ads MCP tools and commands."
|
|
150
|
+
}, indent=2)
|
|
151
|
+
|
|
152
|
+
# IMPORTANT: Start the callback server first by calling our helper function
|
|
153
|
+
# This ensures the server is ready before we provide the URL to the user
|
|
154
|
+
logger.info("Starting callback server for authentication")
|
|
155
|
+
try:
|
|
156
|
+
port = start_callback_server()
|
|
157
|
+
logger.info(f"Callback server started on port {port}")
|
|
158
|
+
|
|
159
|
+
# Generate direct login URL
|
|
160
|
+
auth_manager.redirect_uri = f"http://localhost:{port}/callback" # Ensure port is set correctly
|
|
161
|
+
logger.info(f"Setting redirect URI to {auth_manager.redirect_uri}")
|
|
162
|
+
login_url = auth_manager.get_auth_url()
|
|
163
|
+
logger.info(f"Generated login URL: {login_url}")
|
|
164
|
+
except Exception as e:
|
|
165
|
+
logger.error(f"Failed to start callback server: {e}")
|
|
166
|
+
return json.dumps({
|
|
167
|
+
"message": "❌ Local Authentication Unavailable",
|
|
168
|
+
"error": "Cannot start local callback server for authentication",
|
|
169
|
+
"reason": str(e),
|
|
170
|
+
"solutions": [
|
|
171
|
+
"🌐 Use Pipeboard authentication: Set PIPEBOARD_API_TOKEN environment variable",
|
|
172
|
+
"🔑 Use direct token: Set META_ACCESS_TOKEN environment variable",
|
|
173
|
+
"🔧 Check if another service is using the required ports"
|
|
174
|
+
],
|
|
175
|
+
"authentication_method": "meta_oauth_disabled"
|
|
176
|
+
}, indent=2)
|
|
177
|
+
|
|
178
|
+
# Check if we can exchange for long-lived tokens
|
|
179
|
+
token_exchange_supported = bool(META_APP_SECRET)
|
|
180
|
+
token_duration = "60 days" if token_exchange_supported else "1-2 hours"
|
|
181
|
+
|
|
182
|
+
# Return a special format that helps the LLM format the response properly
|
|
183
|
+
response = {
|
|
184
|
+
"message": "🔗 Click to Authenticate",
|
|
185
|
+
"login_url": login_url,
|
|
186
|
+
"markdown_link": f"[🚀 Authenticate with Meta Ads]({login_url})",
|
|
187
|
+
"instructions": "Click the link above to authenticate with Meta Ads.",
|
|
188
|
+
"server_info": f"Local callback server running on port {port}",
|
|
189
|
+
"token_duration": f"Your token will be valid for approximately {token_duration}",
|
|
190
|
+
"authentication_method": "meta_oauth",
|
|
191
|
+
"what_happens_next": "After clicking, you'll be redirected to Meta's authentication page. Once completed, your token will be automatically saved.",
|
|
192
|
+
"security_note": "This uses a secure local callback server for development purposes."
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
# Wait a moment to ensure the server is fully started
|
|
196
|
+
await asyncio.sleep(1)
|
|
197
|
+
|
|
198
|
+
return json.dumps(response, indent=2)
|
|
@@ -1,135 +0,0 @@
|
|
|
1
|
-
"""Authentication-specific functionality for Meta Ads API."""
|
|
2
|
-
|
|
3
|
-
import json
|
|
4
|
-
import asyncio
|
|
5
|
-
import os
|
|
6
|
-
from .api import meta_api_tool
|
|
7
|
-
from .auth import start_callback_server, shutdown_callback_server, auth_manager, get_current_access_token
|
|
8
|
-
from .server import mcp_server
|
|
9
|
-
from .utils import logger, META_APP_SECRET
|
|
10
|
-
from .pipeboard_auth import pipeboard_auth_manager
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
@mcp_server.tool()
|
|
14
|
-
async def get_login_link(access_token: str = None) -> str:
|
|
15
|
-
"""
|
|
16
|
-
Get a clickable login link for Meta Ads authentication.
|
|
17
|
-
|
|
18
|
-
NOTE: This method should only be used if you're using your own Facebook app.
|
|
19
|
-
If using Pipeboard authentication (recommended), set the PIPEBOARD_API_TOKEN
|
|
20
|
-
environment variable instead (token obtainable via https://pipeboard.co).
|
|
21
|
-
|
|
22
|
-
Args:
|
|
23
|
-
access_token: Meta API access token (optional - will use cached token if not provided)
|
|
24
|
-
|
|
25
|
-
Returns:
|
|
26
|
-
A clickable resource link for Meta authentication
|
|
27
|
-
"""
|
|
28
|
-
# Check if we're using pipeboard authentication
|
|
29
|
-
using_pipeboard = bool(os.environ.get("PIPEBOARD_API_TOKEN", ""))
|
|
30
|
-
|
|
31
|
-
if using_pipeboard:
|
|
32
|
-
# Handle Pipeboard authentication
|
|
33
|
-
# Check if we have a cached token
|
|
34
|
-
cached_token = pipeboard_auth_manager.get_access_token()
|
|
35
|
-
token_status = "No token" if not cached_token else "Valid token"
|
|
36
|
-
|
|
37
|
-
# If we already have a valid token and none was provided, just return success
|
|
38
|
-
if cached_token and not access_token:
|
|
39
|
-
logger.info("get_login_link called with existing valid Pipeboard token")
|
|
40
|
-
return json.dumps({
|
|
41
|
-
"message": "Already authenticated with Pipeboard",
|
|
42
|
-
"token_status": token_status,
|
|
43
|
-
"token_preview": cached_token[:10] + "..." if cached_token else None,
|
|
44
|
-
"authentication_method": "pipeboard"
|
|
45
|
-
}, indent=2)
|
|
46
|
-
|
|
47
|
-
# Initiate the auth flow via Pipeboard
|
|
48
|
-
try:
|
|
49
|
-
auth_data = pipeboard_auth_manager.initiate_auth_flow()
|
|
50
|
-
login_url = auth_data.get("loginUrl")
|
|
51
|
-
|
|
52
|
-
# Return a special format that helps the LLM format the response properly
|
|
53
|
-
response = {
|
|
54
|
-
"login_url": login_url,
|
|
55
|
-
"token_status": token_status,
|
|
56
|
-
"markdown_link": f"[Click here to authenticate with Meta Ads via Pipeboard]({login_url})",
|
|
57
|
-
"message": "IMPORTANT: Please use the Markdown link format in your response to allow the user to click it.",
|
|
58
|
-
"instructions_for_llm": "You must present this link as clickable Markdown to the user using the markdown_link format provided.",
|
|
59
|
-
"authentication_method": "pipeboard",
|
|
60
|
-
"token_duration": "Approximately 60 days",
|
|
61
|
-
"note": "After authenticating, the token will be automatically saved."
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
return json.dumps(response, indent=2)
|
|
65
|
-
except Exception as e:
|
|
66
|
-
logger.error(f"Error initiating Pipeboard auth flow: {e}")
|
|
67
|
-
return json.dumps({
|
|
68
|
-
"error": f"Failed to initiate Pipeboard authentication: {str(e)}",
|
|
69
|
-
"message": "Please check your PIPEBOARD_API_TOKEN environment variable.",
|
|
70
|
-
"authentication_method": "pipeboard"
|
|
71
|
-
}, indent=2)
|
|
72
|
-
else:
|
|
73
|
-
# Original Meta authentication flow
|
|
74
|
-
# Check if we have a cached token
|
|
75
|
-
cached_token = auth_manager.get_access_token()
|
|
76
|
-
token_status = "No token" if not cached_token else "Valid token"
|
|
77
|
-
|
|
78
|
-
# If we already have a valid token and none was provided, just return success
|
|
79
|
-
if cached_token and not access_token:
|
|
80
|
-
logger.info("get_login_link called with existing valid token")
|
|
81
|
-
return json.dumps({
|
|
82
|
-
"message": "Already authenticated",
|
|
83
|
-
"token_status": token_status,
|
|
84
|
-
"token_preview": cached_token[:10] + "...",
|
|
85
|
-
"created_at": auth_manager.token_info.created_at if hasattr(auth_manager, "token_info") else None,
|
|
86
|
-
"expires_in": auth_manager.token_info.expires_in if hasattr(auth_manager, "token_info") else None,
|
|
87
|
-
"authentication_method": "meta_oauth"
|
|
88
|
-
}, indent=2)
|
|
89
|
-
|
|
90
|
-
# IMPORTANT: Start the callback server first by calling our helper function
|
|
91
|
-
# This ensures the server is ready before we provide the URL to the user
|
|
92
|
-
logger.info("Starting callback server for authentication")
|
|
93
|
-
try:
|
|
94
|
-
port = start_callback_server()
|
|
95
|
-
logger.info(f"Callback server started on port {port}")
|
|
96
|
-
|
|
97
|
-
# Generate direct login URL
|
|
98
|
-
auth_manager.redirect_uri = f"http://localhost:{port}/callback" # Ensure port is set correctly
|
|
99
|
-
logger.info(f"Setting redirect URI to {auth_manager.redirect_uri}")
|
|
100
|
-
login_url = auth_manager.get_auth_url()
|
|
101
|
-
logger.info(f"Generated login URL: {login_url}")
|
|
102
|
-
except Exception as e:
|
|
103
|
-
logger.error(f"Failed to start callback server: {e}")
|
|
104
|
-
return json.dumps({
|
|
105
|
-
"error": "Callback server disabled",
|
|
106
|
-
"message": str(e),
|
|
107
|
-
"suggestion": "Use Pipeboard authentication (set PIPEBOARD_API_TOKEN) or provide a direct access token",
|
|
108
|
-
"authentication_method": "meta_oauth_disabled"
|
|
109
|
-
}, indent=2)
|
|
110
|
-
|
|
111
|
-
# Check if we can exchange for long-lived tokens
|
|
112
|
-
token_exchange_supported = bool(META_APP_SECRET)
|
|
113
|
-
token_duration = "60 days" if token_exchange_supported else "1-2 hours"
|
|
114
|
-
|
|
115
|
-
# Return a special format that helps the LLM format the response properly
|
|
116
|
-
response = {
|
|
117
|
-
"login_url": login_url,
|
|
118
|
-
"token_status": token_status,
|
|
119
|
-
"server_status": f"Callback server running on port {port}",
|
|
120
|
-
"markdown_link": f"[Click here to authenticate with Meta Ads]({login_url})",
|
|
121
|
-
"message": "IMPORTANT: Please use the Markdown link format in your response to allow the user to click it.",
|
|
122
|
-
"instructions_for_llm": "You must present this link as clickable Markdown to the user using the markdown_link format provided.",
|
|
123
|
-
"token_exchange": "enabled" if token_exchange_supported else "disabled",
|
|
124
|
-
"token_duration": token_duration,
|
|
125
|
-
"authentication_method": "meta_oauth",
|
|
126
|
-
"token_exchange_message": f"Your authentication token will be valid for approximately {token_duration}." +
|
|
127
|
-
(" Long-lived token exchange is enabled." if token_exchange_supported else
|
|
128
|
-
" For direct Meta authentication, long-lived tokens require META_APP_SECRET. Consider using Pipeboard authentication instead (60-day tokens by default)."),
|
|
129
|
-
"note": "After authenticating, the token will be automatically saved."
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
# Wait a moment to ensure the server is fully started
|
|
133
|
-
await asyncio.sleep(1)
|
|
134
|
-
|
|
135
|
-
return json.dumps(response, indent=2)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|