Purge standalone OAuth setup for Gmail and Gcal
- Deleted gmail_oauth_setup.py and gcal_oauth_setup.py (Skald handles OAuth) - Removed google-auth-oauthlib dependency from requirements.txt and connector.json - Simplified setup_instructions to point to Skald's OAuth flow - Updated docstrings and error messages in *_mcp_server.py (no more refs to *_oauth_setup.py, point to Skald OAuth / env-var instead) - connectors.json: removed oauth_setup.py from files[], updated sha256 hashes
This commit is contained in:
+10
-16
@@ -31,14 +31,11 @@
|
||||
"files": [
|
||||
{
|
||||
"path": "gmail_mcp_server.py",
|
||||
"sha256": "53e6cdbc4ff40a095d29f7538ec23ac8fc10fb83cb28496bb28a83e7c49deaae",
|
||||
"size": 47833
|
||||
"sha256": "e96568dda3d0198be0a34092458dac2aaafee4a7e282a3fb427923f517e536e3",
|
||||
"size": 47789
|
||||
},
|
||||
{
|
||||
"path": "gmail_oauth_setup.py",
|
||||
"sha256": "e488acb289c43a3e6d541140d254451ac5688c0014e8959619375b564afe8748",
|
||||
"size": 3627
|
||||
},
|
||||
"path": "icon_lg.svg",
|
||||
{
|
||||
"path": "icon_lg.svg",
|
||||
"sha256": "93c8d9c8dae96f0206e5ae3fcfe89dff3a08553ed48badf504a5abd09f5596b0",
|
||||
@@ -51,8 +48,8 @@
|
||||
},
|
||||
{
|
||||
"path": "requirements.txt",
|
||||
"sha256": "3f659cc5e5f0543f132699b06ccf9016ffe9afb40f9df4d110e0445b8d20f63e",
|
||||
"size": 82
|
||||
"sha256": "41a908f999095b052865efdcf793cfe7b032a3609c0cdd9f7711b2af5b3b7827",
|
||||
"size": 54
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -85,14 +82,11 @@
|
||||
"files": [
|
||||
{
|
||||
"path": "gcal_mcp_server.py",
|
||||
"sha256": "87b44a407feac90ed073997bd0a558c7002eec0c408d50c45da7d99011dd6107",
|
||||
"size": 38203
|
||||
"sha256": "c526751c8891a8d97ba37d14da282d223be7cd061eb67c76cea6a12672921db0",
|
||||
"size": 38177
|
||||
},
|
||||
{
|
||||
"path": "gcal_oauth_setup.py",
|
||||
"sha256": "4abbfcf6af50cc55ca00d283353b417acc6fee7720aa10abb0fbaa0e4aa79900",
|
||||
"size": 3430
|
||||
},
|
||||
"path": "verify.py",
|
||||
{
|
||||
"path": "verify.py",
|
||||
"sha256": "d994ae435d8a5a3d9cdc85c8f73805a877c1a8818637b2912f6c31c693fdded4",
|
||||
@@ -110,8 +104,8 @@
|
||||
},
|
||||
{
|
||||
"path": "requirements.txt",
|
||||
"sha256": "3f659cc5e5f0543f132699b06ccf9016ffe9afb40f9df4d110e0445b8d20f63e",
|
||||
"size": 82
|
||||
"sha256": "41a908f999095b052865efdcf793cfe7b032a3609c0cdd9f7711b2af5b3b7827",
|
||||
"size": 54
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@@ -10,12 +10,10 @@
|
||||
"tags": ["calendar", "mcp", "local", "google"],
|
||||
"dependencies": [
|
||||
"google-api-python-client>=2.150.0",
|
||||
"google-auth>=2.35.0",
|
||||
"google-auth-oauthlib>=1.2.0"
|
||||
"google-auth>=2.35.0"
|
||||
],
|
||||
"setup_instructions": [
|
||||
"Install dependencies: pip install -r requirements.txt",
|
||||
"Run: python3 gcal_oauth_setup.py (optional, for standalone use — Skald handles OAuth)"
|
||||
"Activated from Skald: an admin configures the Google sign-in provider, then each user signs in from the connector page (OAuth handled by Skald)."
|
||||
],
|
||||
"docs": [
|
||||
{
|
||||
|
||||
@@ -13,7 +13,6 @@ Capabilities (callable as `mcp__gcal__<tool>`):
|
||||
|
||||
Skald mode: Skald injects credentials via GCAL_CREDS_JSON env var (authorized_user JSON).
|
||||
Standalone mode: reads from GOOGLE_CREDS_PATH or ./secrets/google_creds.json.
|
||||
Run scripts/gcal_oauth_setup.py to (re-)authenticate (standalone).
|
||||
|
||||
Required OAuth scopes:
|
||||
https://www.googleapis.com/auth/calendar
|
||||
@@ -282,7 +281,7 @@ def _format_google_error(e: Exception, api_label: str) -> str:
|
||||
if RefreshError is not None and isinstance(e, RefreshError):
|
||||
return (
|
||||
f"Error: {api_label} API token refresh failed (the refresh token may have been revoked "
|
||||
"or expired). Re-run scripts/gcal_oauth_setup.py to re-authenticate."
|
||||
"or expired). Re-authenticate via Skald's OAuth flow (Sign in from the connector page)."
|
||||
)
|
||||
|
||||
if HttpError is not None and isinstance(e, HttpError):
|
||||
@@ -290,13 +289,12 @@ def _format_google_error(e: Exception, api_label: str) -> str:
|
||||
if status == 401:
|
||||
return (
|
||||
f"Error: {api_label} API rejected the access token (401). The OAuth token is invalid "
|
||||
"or revoked. Re-run scripts/gcal_oauth_setup.py to re-authenticate."
|
||||
)
|
||||
"or revoked. Re-authenticate via Skald's OAuth flow (Sign in from the connector page)." )
|
||||
if status == 403:
|
||||
return (
|
||||
f"Error: {api_label} API returned 403 Forbidden. The OAuth scopes granted are "
|
||||
"insufficient for this operation, or the Calendar API is disabled in the Google Cloud "
|
||||
"Console. Verify the scopes in scripts/gcal_oauth_setup.py and the API enablement."
|
||||
"Console. Verify the scopes in the Google OAuth provider config and the API enablement."
|
||||
)
|
||||
if status == 404:
|
||||
return (
|
||||
@@ -350,9 +348,8 @@ def _gcal_status(args: dict | None = None) -> str:
|
||||
except Exception as e:
|
||||
return _status_report("❌", "AUTH_OR_API_ERROR", "action needed",
|
||||
f"The Calendar API did not respond to the probe call: {_format_google_error(e, 'Calendar')}",
|
||||
["Run scripts/gcal_oauth_setup.py to refresh / re-issue credentials.",
|
||||
["Re-authenticate via Skald's OAuth flow (Sign in from the connector page).",
|
||||
"If credentials are valid, verify the Google Calendar API is enabled in the Google Cloud Console."])
|
||||
|
||||
items = result.get("items", []) if isinstance(result, dict) else []
|
||||
primary = next((c for c in items if c.get("primary")), None)
|
||||
suffix = f"\nAccount: {primary.get('id')}" if primary else ""
|
||||
|
||||
@@ -1,106 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Generate a Google OAuth token for the Calendar API (read + write).
|
||||
|
||||
This script runs a local OAuth flow that:
|
||||
1. Opens your browser automatically to the Google authorization page
|
||||
2. Handles the callback via a local HTTP server
|
||||
3. Saves the resulting token to ./secrets/google_creds.json
|
||||
|
||||
Required OAuth scope: https://www.googleapis.com/auth/calendar
|
||||
(full access — needed for create, update, delete, respond).
|
||||
|
||||
No manual copy-paste required.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
|
||||
SCOPES = [
|
||||
"https://www.googleapis.com/auth/calendar",
|
||||
]
|
||||
|
||||
_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
SECRET_PATH = os.path.join(_ROOT, "secrets", "google_creds.json")
|
||||
_OAUTH_CLIENT_PATH = os.path.join(_ROOT, "secrets", "google_oauth_client.json")
|
||||
|
||||
|
||||
def _load_oauth_client() -> tuple[str, str]:
|
||||
if not os.path.exists(_OAUTH_CLIENT_PATH):
|
||||
print(f"Missing OAuth client file: {_OAUTH_CLIENT_PATH}")
|
||||
print('Create it with: {"client_id": "...", "client_secret": "..."}')
|
||||
sys.exit(1)
|
||||
with open(_OAUTH_CLIENT_PATH) as f:
|
||||
data = json.load(f)
|
||||
return data["client_id"], data["client_secret"]
|
||||
|
||||
|
||||
def main() -> None:
|
||||
try:
|
||||
from google.auth.transport.requests import Request
|
||||
from google.oauth2.credentials import Credentials
|
||||
from google_auth_oauthlib.flow import InstalledAppFlow
|
||||
except ImportError as e:
|
||||
print(f"Missing dependencies: {e}")
|
||||
print("Install with: pip install google-auth google-auth-oauthlib google-api-python-client")
|
||||
sys.exit(1)
|
||||
|
||||
creds = None
|
||||
|
||||
# Try to load existing credentials first.
|
||||
if os.path.exists(SECRET_PATH):
|
||||
print(f"Existing credentials found at {SECRET_PATH}")
|
||||
try:
|
||||
creds = Credentials.from_authorized_user_file(SECRET_PATH, SCOPES)
|
||||
except Exception:
|
||||
creds = None
|
||||
|
||||
if creds and creds.valid:
|
||||
print("Credentials are already valid!")
|
||||
print(f" Scopes: {creds.scopes}")
|
||||
return
|
||||
|
||||
if creds and creds.expired and creds.refresh_token:
|
||||
print("Token expired. Attempting refresh...")
|
||||
try:
|
||||
creds.refresh(Request())
|
||||
print("Token refreshed successfully!")
|
||||
except Exception as e:
|
||||
print(f"Refresh failed: {e}")
|
||||
creds = None
|
||||
|
||||
if not creds or not creds.valid:
|
||||
client_id, client_secret = _load_oauth_client()
|
||||
flow = InstalledAppFlow.from_client_config(
|
||||
{
|
||||
"installed": {
|
||||
"client_id": client_id,
|
||||
"client_secret": client_secret,
|
||||
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
|
||||
"token_uri": "https://oauth2.googleapis.com/token",
|
||||
"redirect_uris": ["http://localhost"],
|
||||
}
|
||||
},
|
||||
SCOPES,
|
||||
)
|
||||
|
||||
print("\nOpening browser for Google authorization...")
|
||||
creds = flow.run_local_server(
|
||||
port=0,
|
||||
open_browser=True,
|
||||
prompt="consent",
|
||||
access_type="offline",
|
||||
)
|
||||
|
||||
os.makedirs(os.path.dirname(SECRET_PATH), exist_ok=True)
|
||||
with open(SECRET_PATH, "w") as f:
|
||||
f.write(creds.to_json())
|
||||
|
||||
print(f"\n✅ Google Calendar OAuth token saved to {SECRET_PATH}")
|
||||
print(f" Scopes: {creds.scopes}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,3 +1,2 @@
|
||||
google-api-python-client>=2.150.0
|
||||
google-auth>=2.35.0
|
||||
google-auth-oauthlib>=1.2.0
|
||||
|
||||
@@ -11,12 +11,10 @@
|
||||
],
|
||||
"dependencies": [
|
||||
"google-api-python-client>=2.150.0",
|
||||
"google-auth>=2.35.0",
|
||||
"google-auth-oauthlib>=1.2.0"
|
||||
"google-auth>=2.35.0"
|
||||
],
|
||||
"setup_instructions": [
|
||||
"Install dependencies: pip install -r requirements.txt",
|
||||
"Run: python3 gmail_oauth_setup.py (optional, for standalone use — Skald handles OAuth)"
|
||||
"Activated from Skald: an admin configures the Google sign-in provider, then each user signs in from the connector page (OAuth handled by Skald)."
|
||||
],
|
||||
"docs": [
|
||||
{
|
||||
|
||||
@@ -17,7 +17,6 @@ Provides read, modify, and send access to Gmail via the Gmail API v1.
|
||||
|
||||
Skald mode: Skald injects credentials via GMAIL_CREDS_JSON env var (authorized_user JSON).
|
||||
Standalone mode: reads from GMAIL_CREDS_PATH or ./secrets/gmail_creds.json.
|
||||
Run scripts/gmail_oauth_setup.py first to generate the OAuth token (standalone).
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
@@ -308,7 +307,7 @@ def _format_google_error(e: Exception, api_label: str) -> str:
|
||||
if RefreshError is not None and isinstance(e, RefreshError):
|
||||
return (
|
||||
f"Error: {api_label} API token refresh failed (the refresh token may have been revoked "
|
||||
"or expired). Re-run scripts/gmail_oauth_setup.py to re-authenticate."
|
||||
"or expired). Re-authenticate via Skald's OAuth flow (Sign in from the connector page)."
|
||||
)
|
||||
|
||||
if HttpError is not None and isinstance(e, HttpError):
|
||||
@@ -316,13 +315,12 @@ def _format_google_error(e: Exception, api_label: str) -> str:
|
||||
if status == 401:
|
||||
return (
|
||||
f"Error: {api_label} API rejected the access token (401). The OAuth token is invalid "
|
||||
"or revoked. Re-run scripts/gmail_oauth_setup.py to re-authenticate."
|
||||
)
|
||||
"or revoked. Re-authenticate via Skald's OAuth flow (Sign in from the connector page)." )
|
||||
if status == 403:
|
||||
return (
|
||||
f"Error: {api_label} API returned 403 Forbidden. The OAuth scopes granted are "
|
||||
"insufficient for this operation, or the Gmail API is disabled in the Google Cloud "
|
||||
"Console. Verify the scopes in scripts/gmail_oauth_setup.py and the API enablement."
|
||||
"Console. Verify the scopes in the Google OAuth provider config and the API enablement."
|
||||
)
|
||||
if status == 404:
|
||||
return (
|
||||
@@ -482,9 +480,8 @@ def _gmail_status(args: dict | None = None) -> str:
|
||||
except Exception as e:
|
||||
return _status_report("❌", "AUTH_OR_API_ERROR", "action needed",
|
||||
f"The Gmail API did not respond to the probe call: {_format_google_error(e, 'Gmail')}",
|
||||
["Run scripts/gmail_oauth_setup.py to refresh / re-issue credentials.",
|
||||
["Re-authenticate via Skald's OAuth flow (Sign in from the connector page).",
|
||||
"If credentials are valid, verify the Gmail API is enabled in the Google Cloud Console."])
|
||||
|
||||
email = profile.get("emailAddress", "?")
|
||||
return _status_report("✅", "READY", "ok",
|
||||
"Google Gmail integration is operational: credentials load, the access token refreshes "
|
||||
|
||||
@@ -1,108 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Generate a Google OAuth token for Gmail API.
|
||||
|
||||
This script runs a local OAuth flow that:
|
||||
1. Opens your browser automatically to the Google authorization page
|
||||
2. Handles the callback via a local HTTP server
|
||||
3. Saves the resulting token to ./secrets/gmail_creds.json
|
||||
|
||||
No manual copy-paste required.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
|
||||
SCOPES = [
|
||||
"https://www.googleapis.com/auth/gmail.modify",
|
||||
"https://www.googleapis.com/auth/gmail.labels",
|
||||
]
|
||||
|
||||
_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
SECRET_PATH = os.path.join(_ROOT, "secrets", "gmail_creds.json")
|
||||
_OAUTH_CLIENT_PATH = os.path.join(_ROOT, "secrets", "google_oauth_client.json")
|
||||
|
||||
|
||||
def _load_oauth_client() -> tuple[str, str]:
|
||||
if not os.path.exists(_OAUTH_CLIENT_PATH):
|
||||
print(f"Missing OAuth client file: {_OAUTH_CLIENT_PATH}")
|
||||
print("Create it with: {\"client_id\": \"...\", \"client_secret\": \"...\"}")
|
||||
sys.exit(1)
|
||||
with open(_OAUTH_CLIENT_PATH) as f:
|
||||
data = json.load(f)
|
||||
return data["client_id"], data["client_secret"]
|
||||
|
||||
|
||||
def main() -> None:
|
||||
# Lazy-import so we can show helpful errors if not installed.
|
||||
try:
|
||||
from google.auth.transport.requests import Request
|
||||
from google.oauth2.credentials import Credentials
|
||||
from google_auth_oauthlib.flow import InstalledAppFlow
|
||||
except ImportError as e:
|
||||
print(f"Missing dependencies: {e}")
|
||||
print("Install with: pip3 install google-auth google-auth-oauthlib google-api-python-client")
|
||||
sys.exit(1)
|
||||
|
||||
creds = None
|
||||
|
||||
# Try to load existing credentials first, in case they have refresh token.
|
||||
if os.path.exists(SECRET_PATH):
|
||||
print(f"Existing credentials found at {SECRET_PATH}")
|
||||
try:
|
||||
creds = Credentials.from_authorized_user_file(SECRET_PATH, SCOPES)
|
||||
except Exception:
|
||||
creds = None
|
||||
|
||||
# If creds exist and are valid, we're good.
|
||||
if creds and creds.valid:
|
||||
print("Credentials are already valid!")
|
||||
return
|
||||
|
||||
# If creds exist but expired, try to refresh.
|
||||
if creds and creds.expired and creds.refresh_token:
|
||||
print("Token expired. Attempting refresh...")
|
||||
try:
|
||||
creds.refresh(Request())
|
||||
print("Token refreshed successfully!")
|
||||
except Exception as e:
|
||||
print(f"Refresh failed: {e}")
|
||||
creds = None
|
||||
|
||||
if not creds or not creds.valid:
|
||||
client_id, client_secret = _load_oauth_client()
|
||||
# Start OAuth flow using local server (opens browser automatically).
|
||||
flow = InstalledAppFlow.from_client_config(
|
||||
{
|
||||
"installed": {
|
||||
"client_id": client_id,
|
||||
"client_secret": client_secret,
|
||||
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
|
||||
"token_uri": "https://oauth2.googleapis.com/token",
|
||||
"redirect_uris": ["http://localhost"],
|
||||
}
|
||||
},
|
||||
SCOPES,
|
||||
)
|
||||
|
||||
print("\nOpening browser for Google authorization...")
|
||||
creds = flow.run_local_server(
|
||||
port=0, # pick a random available port
|
||||
open_browser=True,
|
||||
prompt="consent",
|
||||
access_type="offline",
|
||||
)
|
||||
|
||||
# Save credentials.
|
||||
os.makedirs(os.path.dirname(SECRET_PATH), exist_ok=True)
|
||||
with open(SECRET_PATH, "w") as f:
|
||||
f.write(creds.to_json())
|
||||
|
||||
print(f"\n✅ Gmail OAuth token saved to {SECRET_PATH}")
|
||||
print(f" Scopes: {creds.scopes}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,3 +1,2 @@
|
||||
google-api-python-client>=2.150.0
|
||||
google-auth>=2.35.0
|
||||
google-auth-oauthlib>=1.2.0
|
||||
|
||||
Reference in New Issue
Block a user