#!/usr/bin/env python3 import argparse import os from pathlib import Path from huggingface_hub import HfApi, whoami, create_repo, upload_folder def repo_url(space_id: str) -> str: return f"https://huggingface.co/spaces/{space_id}" def app_url(space_id: str) -> str: owner, name = space_id.split("/", 1) return f"https://{owner}-{name}.hf.space" def main() -> None: parser = argparse.ArgumentParser(description="Create/sync the mcp-bridge Hugging Face Docker Space.") parser.add_argument("--space-id", default="patdev/mcp-bridge", help="HF Space ID, e.g. patdev/mcp-bridge") parser.add_argument("--api-key", default=os.getenv("MCP_API_KEY", ""), help="Value for Space secret MCP_API_KEY") parser.add_argument("--oauth-signing-key", default=os.getenv("MCP_OAUTH_SIGNING_KEY", ""), help="Optional Space secret MCP_OAUTH_SIGNING_KEY") parser.add_argument("--public-base-url", default="", help="Public base URL, defaults to https://owner-name.hf.space") parser.add_argument("--chatgpt-callback-url", default="", help="Optional exact ChatGPT OAuth callback URL allowlist. Not required by default; ChatGPT callback prefix is accepted.") parser.add_argument("--oauth-client-id", default=os.getenv("OAUTH_STATIC_CLIENT_ID", "chatgpt"), help="Manual OAuth client ID for ChatGPT when DCR is unavailable") parser.add_argument("--oauth-client-secret", default=os.getenv("OAUTH_STATIC_CLIENT_SECRET", ""), help="Optional manual OAuth client secret") parser.add_argument("--restart", action="store_true", help="Restart the Space after upload") args = parser.parse_args() if "/" not in args.space_id: raise SystemExit("--space-id must look like owner/name") if not args.api_key: raise SystemExit("Missing --api-key or MCP_API_KEY env var") root = Path(__file__).resolve().parents[1] api = HfApi() me = whoami() print(f"Authenticated as: {me.get('name') or me}") print(f"Space ID: {args.space_id}") print("Creating Space if needed...") create_repo(args.space_id, repo_type="space", space_sdk="docker", exist_ok=True) public_base = (args.public_base_url or app_url(args.space_id)).rstrip("/") print("Setting Space Secret MCP_API_KEY...") api.add_space_secret(repo_id=args.space_id, key="MCP_API_KEY", value=args.api_key) if args.oauth_signing_key: print("Setting Space Secret MCP_OAUTH_SIGNING_KEY...") api.add_space_secret(repo_id=args.space_id, key="MCP_OAUTH_SIGNING_KEY", value=args.oauth_signing_key) def set_space_var(key: str, value: str) -> None: try: api.add_space_variable(repo_id=args.space_id, key=key, value=value) except AttributeError: # Older huggingface_hub fallback: harmless, but secret instead of variable. api.add_space_secret(repo_id=args.space_id, key=key, value=value) print(f"Setting Space Variable PUBLIC_BASE_URL={public_base}") set_space_var("PUBLIC_BASE_URL", public_base) if args.oauth_client_id: print(f"Setting Space Variable OAUTH_STATIC_CLIENT_ID={args.oauth_client_id}") set_space_var("OAUTH_STATIC_CLIENT_ID", args.oauth_client_id) if args.chatgpt_callback_url: print("Setting Space Variable OAUTH_ALLOWED_REDIRECT_URIS=") set_space_var("OAUTH_ALLOWED_REDIRECT_URIS", args.chatgpt_callback_url.strip()) else: print("Using default ChatGPT redirect prefix allow: https://chatgpt.com/connector/oauth/*") set_space_var("OAUTH_ALLOW_CHATGPT_REDIRECT_PREFIX", "true") if args.oauth_client_secret: print("Setting Space Secret OAUTH_STATIC_CLIENT_SECRET") api.add_space_secret(repo_id=args.space_id, key="OAUTH_STATIC_CLIENT_SECRET", value=args.oauth_client_secret) print("Uploading project files...") info = upload_folder( repo_id=args.space_id, repo_type="space", folder_path=str(root), path_in_repo=".", ignore_patterns=[".git", ".hf", "__pycache__", "*.pyc", ".env", "*.zip"], commit_message="Update mcp-bridge OAuth server", ) print(f"Uploaded: {getattr(info, 'commit_url', info)}") if args.restart: print("Restarting Space...") api.restart_space(repo_id=args.space_id) print("\nDone.") print(f"Space: {repo_url(args.space_id)}") print(f"MCP SSE: {public_base}/sse") if __name__ == "__main__": main()