""" Strava OAuth2 Authorization Helper This script guides you through the Strava OAuth2 flow to obtain a refresh token with the correct scopes for the MCP server. Required scopes: - read ā basic athlete profile - activity:read ā read your activities - activity:read_all ā read private activities (optional) Usage: uv run get_token.py """ import os import webbrowser import httpx from http.server import HTTPServer, BaseHTTPRequestHandler from urllib.parse import urlparse, parse_qs from dotenv import load_dotenv from strava_mcp_server.config import get_config_file, ensure_config_dir # Load config: platform config dir first, then local .env (local overrides) load_dotenv( get_config_file() ) # e.g. ~/Library/Application Support/strava-mcp-server/config.env load_dotenv(override=False) # local .env overrides if present CLIENT_ID = os.getenv("STRAVA_CLIENT_ID") CLIENT_SECRET = os.getenv("STRAVA_CLIENT_SECRET") REDIRECT_URI = "http://localhost:8765/callback" SCOPES = "profile:read_all,activity:read_all,activity:read,profile:write" class SetupHandler(BaseHTTPRequestHandler): setup_done = False client_id = "" client_secret = "" def do_GET(self): if self.path == "/setup" or self.path == "/": self.send_response(200) self.send_header("Content-Type", "text/html; charset=utf-8") self.end_headers() self.wfile.write(self._get_setup_page().encode("utf-8")) elif self.path.startswith("/save"): parsed = urlparse(self.path) params = parse_qs(parsed.query) if "id" in params and "secret" in params: SetupHandler.client_id = params["id"][0].strip() SetupHandler.client_secret = params["secret"][0].strip() SetupHandler.setup_done = True self.send_response(200) self.send_header("Content-Type", "text/html; charset=utf-8") self.end_headers() self.wfile.write( """
Redirecting to Strava Authorization...
localhost.Your Strava account is now connected to the MCP server.
UPDATED .ENV CONTENT:
STRAVA_CLIENT_ID={self.client_id}
STRAVA_CLIENT_SECRET={self.client_secret}
STRAVA_REFRESH_TOKEN={refresh_token}
Automatically saved to:
{CallbackHandler.config_path}
You can now close this window and restart the server.
""".encode("utf-8") ) except Exception as e: CallbackHandler.error = str(e) self.send_response(500) self.end_headers() self.wfile.write(f"Error exchanging token: {e}".encode()) else: error_msg = params.get("error", ["unknown"])[0] self.send_response(400) self.end_headers() self.wfile.write(f"Error: {error_msg}".encode()) def log_message(self, format, *args): pass # Suppress server logs def save_to_env(client_id, client_secret, refresh_token=None): # Always save to the platform config directory so uvx finds it ensure_config_dir() config_path = get_config_file() lines = [] if config_path.exists(): with open(config_path, "r") as f: lines = f.readlines() keys_to_set = { "STRAVA_CLIENT_ID": client_id, "STRAVA_CLIENT_SECRET": client_secret, } if refresh_token: keys_to_set["STRAVA_REFRESH_TOKEN"] = refresh_token new_lines = [] seen_keys = set() for line in lines: matched = False for key, value in keys_to_set.items(): if line.startswith(f"{key}="): new_lines.append(f"{key}={value}\n") seen_keys.add(key) matched = True break if not matched: new_lines.append(line) for key, value in keys_to_set.items(): if key not in seen_keys: new_lines.append(f"{key}={value}\n") with open(config_path, "w") as f: f.writelines(new_lines) saved_keys = ", ".join(keys_to_set.keys()) print(f"š Saved to {config_path}: {saved_keys}") def main(): global CLIENT_ID, CLIENT_SECRET # 1. Start Setup Wizard if credentials missing if not CLIENT_ID or not CLIENT_SECRET: print( "ā¹ļø Missing credentials. Starting setup wizard at http://localhost:8765 ..." ) print("Please enter your Client ID and Secret in the browser window.") webbrowser.open("http://localhost:8765/setup") HTTPServer.allow_reuse_address = True setup_server = HTTPServer(("localhost", 8765), SetupHandler) try: while not SetupHandler.setup_done: setup_server.handle_request() finally: setup_server.server_close() CLIENT_ID = SetupHandler.client_id CLIENT_SECRET = SetupHandler.client_secret save_to_env(CLIENT_ID, CLIENT_SECRET) # 2. Proceed to Strava OAuth auth_url = ( f"https://www.strava.com/oauth/authorize" f"?client_id={CLIENT_ID}" f"&redirect_uri={REDIRECT_URI}" f"&response_type=code" f"&approval_prompt=force" f"&scope={SCOPES}" ) CallbackHandler.client_id = CLIENT_ID CallbackHandler.client_secret = CLIENT_SECRET CallbackHandler.tokens = {} CallbackHandler.error = None CallbackHandler.config_path = str(get_config_file()) print("\n" + "=" * 60) print(" Strava OAuth2 Authorization") print("=" * 60) print("\nOpening Strava in your browser for final authentication...") webbrowser.open(auth_url) HTTPServer.allow_reuse_address = True server = HTTPServer(("localhost", 8765), CallbackHandler) try: print("Waiting for Strava callback...") while not CallbackHandler.tokens and not CallbackHandler.error: server.handle_request() finally: server.server_close() if not CallbackHandler.error and CallbackHandler.tokens: data = CallbackHandler.tokens refresh_token = data.get("refresh_token") if refresh_token: save_to_env(CLIENT_ID, CLIENT_SECRET, refresh_token) print("\nā Setup successful! All tokens saved to .env") else: print("\nā Error: No refresh token in response.") elif CallbackHandler.error: print(f"\nā Error: {CallbackHandler.error}") if __name__ == "__main__": main()