nexus_hf_mcp_bridge/README.md ADDED
@@ -0,0 +1,83 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # NEXUS HF MCP Bridge
2
+
3
+ Clean, sovereign Hugging Face integration for the **NEXUS Visual Weaver** Space.
4
+
5
+ ## Features
6
+
7
+ - **OAuth Authentication**: Device Code + PKCE flow using the "X - GROK integration for HF" app
8
+ - **Token Management**: Space-friendly token storage with environment variable support
9
+ - **Repository Operations**: Upload, create/update files, list repos using `huggingface_hub`
10
+ - **MCP Tool Exposure**: Ready to be used as MCP tools inside Gradio Spaces (`mcp_server=True`)
11
+
12
+ ## Folder Structure
13
+
14
+ ```
15
+ nexus_hf_mcp_bridge/
16
+ ├── __init__.py
17
+ ├── hf_oauth.py # OAuth Device Code + PKCE
18
+ ├── token_manager.py # Token storage & refresh
19
+ ├── hf_repo_tools.py # huggingface_hub operations
20
+ ├── mcp_bridge.py # Main bridge + MCP tools
21
+ └── README.md
22
+ ```
23
+
24
+ ## Quick Start (Inside the Space)
25
+
26
+ ```python
27
+ from nexus_hf_mcp_bridge import nexus_hf_bridge
28
+
29
+ # 1. Authenticate (one-time)
30
+ result = nexus_hf_bridge.tool_authenticate()
31
+
32
+ # 2. Use tools
33
+ nexus_hf_bridge.tool_create_or_update_file(
34
+ repo_id="your-username/your-repo",
35
+ path_in_repo="example.txt",
36
+ content="Hello from NEXUS HF MCP Bridge!"
37
+ )
38
+ ```
39
+
40
+ ## Integration into `app.py`
41
+
42
+ ```python
43
+ from nexus_hf_mcp_bridge import nexus_hf_bridge
44
+ import gradio as gr
45
+
46
+ with gr.Blocks() as demo:
47
+ gr.Markdown("# NEXUS Visual Weaver - HF Bridge")
48
+
49
+ with gr.Tab("HF Tools"):
50
+ repo = gr.Textbox(label="Repository ID")
51
+ path = gr.Textbox(label="Path in Repo")
52
+ content = gr.Textbox(label="Content", lines=8)
53
+ btn = gr.Button("Create / Update File")
54
+ out = gr.JSON()
55
+
56
+ btn.click(
57
+ nexus_hf_bridge.tool_create_or_update_file,
58
+ inputs=[repo, path, content],
59
+ outputs=out
60
+ )
61
+
62
+ demo.launch(mcp_server=True)
63
+ ```
64
+
65
+ ## Authentication
66
+
67
+ The bridge supports two modes:
68
+
69
+ 1. **Interactive** (Device Code flow) — Good for development
70
+ 2. **Environment Variable** (recommended for Spaces):
71
+ ```bash
72
+ HF_OAUTH_ACCESS_TOKEN=hf_xxxxxxxxxxxxxxxx
73
+ ```
74
+
75
+ ## Security Notes
76
+
77
+ - Never commit tokens
78
+ - Use Space Secrets for production
79
+ - Token refresh logic is prepared for future extension
80
+
81
+ ## License
82
+
83
+ Internal NEXUS OS Project
nexus_hf_mcp_bridge/__init__.py ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ NEXUS HF MCP Bridge
3
+
4
+ A clean, sovereign Hugging Face integration layer for the NEXUS Visual Weaver Space.
5
+
6
+ Provides:
7
+ - OAuth Device Code + PKCE authentication
8
+ - Token management (Space-friendly)
9
+ - High-level huggingface_hub operations
10
+ - MCP tool exposure for Gradio Spaces
11
+
12
+ Author: NEXUS OS Team
13
+ """
14
+
15
+ from .hf_oauth import HFOAuthClient, HFOAuthToken, authenticate_hf
16
+ from .token_manager import HFTokenManager
17
+ from .hf_repo_tools import HFRepoTools
18
+ from .mcp_bridge import NEXUS_HF_MCP_Bridge, nexus_hf_bridge
19
+
20
+ __version__ = "0.1.0"
21
+ __all__ = [
22
+ "HFOAuthClient",
23
+ "HFOAuthToken",
24
+ "authenticate_hf",
25
+ "HFTokenManager",
26
+ "HFRepoTools",
27
+ "NEXUS_HF_MCP_Bridge",
28
+ "nexus_hf_bridge",
29
+ ]
nexus_hf_mcp_bridge/hf_oauth.py ADDED
@@ -0,0 +1,121 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ hf_oauth.py
3
+ Custom HF MCP Bridge - OAuth Layer
4
+ Handles Device Code + PKCE flow for the "X - GROK integration for HF" OAuth app.
5
+
6
+ This module is designed to run inside the NEXUS Visual Weaver Space
7
+ (which has full internet access).
8
+ """
9
+
10
+ import requests
11
+ import time
12
+ from typing import Optional, Dict, Any
13
+ from dataclasses import dataclass
14
+
15
+ CLIENT_ID = "6b41a3bf-689f-4b48-bbce-b39adc7b515d"
16
+ DEVICE_CODE_URL = "https://huggingface.co/oauth/device"
17
+ TOKEN_URL = "https://huggingface.co/oauth/token"
18
+
19
+
20
+ @dataclass
21
+ class HFOAuthToken:
22
+ access_token: str
23
+ token_type: str = "bearer"
24
+ expires_in: int = 0
25
+ refresh_token: Optional[str] = None
26
+ scope: Optional[str] = None
27
+
28
+
29
+ class HFOAuthClient:
30
+ """
31
+ Handles OAuth authentication for Hugging Face using Device Code flow.
32
+ Suitable for CLI agents and Spaces.
33
+ """
34
+
35
+ def __init__(self, client_id: str = CLIENT_ID):
36
+ self.client_id = client_id
37
+ self._current_token: Optional[HFOAuthToken] = None
38
+
39
+ def start_device_flow(self) -> Dict[str, Any]:
40
+ """Step 1: Request device code and user verification URL."""
41
+ response = requests.post(
42
+ DEVICE_CODE_URL,
43
+ data={"client_id": self.client_id},
44
+ timeout=10
45
+ )
46
+ response.raise_for_status()
47
+ return response.json()
48
+
49
+ def poll_for_token(self, device_code: str, interval: int = 5, max_wait: int = 300) -> Optional[HFOAuthToken]:
50
+ """
51
+ Step 2: Poll the token endpoint until the user authorizes.
52
+ """
53
+ start_time = time.time()
54
+ while time.time() - start_time < max_wait:
55
+ response = requests.post(
56
+ TOKEN_URL,
57
+ data={
58
+ "grant_type": "urn:ietf:params:oauth:grant-type:device_code",
59
+ "device_code": device_code,
60
+ "client_id": self.client_id,
61
+ },
62
+ timeout=10
63
+ )
64
+
65
+ if response.status_code == 200:
66
+ data = response.json()
67
+ token = HFOAuthToken(
68
+ access_token=data["access_token"],
69
+ token_type=data.get("token_type", "bearer"),
70
+ expires_in=data.get("expires_in", 0),
71
+ refresh_token=data.get("refresh_token"),
72
+ scope=data.get("scope"),
73
+ )
74
+ self._current_token = token
75
+ return token
76
+
77
+ elif response.status_code == 400:
78
+ error = response.json().get("error")
79
+ if error == "authorization_pending":
80
+ time.sleep(interval)
81
+ continue
82
+ elif error == "slow_down":
83
+ time.sleep(interval + 5)
84
+ continue
85
+ else:
86
+ print(f"[OAuth] Error: {response.json()}")
87
+ return None
88
+ else:
89
+ print(f"[OAuth] Unexpected status: {response.status_code}")
90
+ return None
91
+
92
+ print("[OAuth] Timeout waiting for user authorization.")
93
+ return None
94
+
95
+ def get_token(self) -> Optional[HFOAuthToken]:
96
+ """Return the current valid token (if any)."""
97
+ return self._current_token
98
+
99
+ def is_authenticated(self) -> bool:
100
+ return self._current_token is not None and bool(self._current_token.access_token)
101
+
102
+
103
+ # Convenience function for quick usage
104
+ def authenticate_hf() -> Optional[HFOAuthToken]:
105
+ """
106
+ Full Device Code flow in one call.
107
+ Returns HFOAuthToken or None.
108
+ """
109
+ client = HFOAuthClient()
110
+ device_data = client.start_device_flow()
111
+
112
+ print("\n=== Hugging Face OAuth ===")
113
+ print(f"Open this URL: {device_data['verification_uri']}")
114
+ print(f"Enter code: {device_data['user_code']}\n")
115
+
116
+ input("Press Enter after you have authorized in the browser...")
117
+
118
+ token = client.poll_for_token(device_data["device_code"])
119
+ if token:
120
+ print("[+] Successfully authenticated with Hugging Face!")
121
+ return token
nexus_hf_mcp_bridge/hf_repo_tools.py ADDED
@@ -0,0 +1,104 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ hf_repo_tools.py
3
+ Custom HF MCP Bridge - Repository Operations
4
+
5
+ Core Hugging Face Hub operations (push, update, list, etc.)
6
+ using huggingface_hub library + our OAuth token.
7
+ """
8
+
9
+ from typing import Optional, List, Dict, Any
10
+ from huggingface_hub import HfApi, upload_file, create_repo, list_repo_files
11
+ import os
12
+
13
+
14
+ class HFRepoTools:
15
+ """
16
+ High-level wrapper around huggingface_hub for common operations.
17
+ Uses token from HFTokenManager.
18
+ """
19
+
20
+ def __init__(self, token: str):
21
+ self.token = token
22
+ self.api = HfApi(token=token)
23
+
24
+ def upload_file_to_repo(
25
+ self,
26
+ local_path: str,
27
+ repo_id: str,
28
+ path_in_repo: str,
29
+ repo_type: str = "model",
30
+ commit_message: Optional[str] = None
31
+ ) -> bool:
32
+ """Upload a single file to a Hugging Face repo."""
33
+ try:
34
+ upload_file(
35
+ path_or_fileobj=local_path,
36
+ path_in_repo=path_in_repo,
37
+ repo_id=repo_id,
38
+ repo_type=repo_type,
39
+ token=self.token,
40
+ commit_message=commit_message or f"Upload {path_in_repo}"
41
+ )
42
+ return True
43
+ except Exception as e:
44
+ print(f"[HFRepoTools] Upload failed: {e}")
45
+ return False
46
+
47
+ def create_or_update_file(
48
+ self,
49
+ repo_id: str,
50
+ path_in_repo: str,
51
+ content: str,
52
+ commit_message: str = "Update file via NEXUS MCP Bridge"
53
+ ) -> bool:
54
+ """Create or update a text file in a repo."""
55
+ try:
56
+ self.api.upload_file(
57
+ path_or_fileobj=content.encode("utf-8"),
58
+ path_in_repo=path_in_repo,
59
+ repo_id=repo_id,
60
+ token=self.token,
61
+ commit_message=commit_message
62
+ )
63
+ return True
64
+ except Exception as e:
65
+ print(f"[HFRepoTools] create_or_update_file failed: {e}")
66
+ return False
67
+
68
+ def list_files(self, repo_id: str, revision: str = "main") -> List[str]:
69
+ """List files in a repository."""
70
+ try:
71
+ files = list_repo_files(repo_id=repo_id, revision=revision, token=self.token)
72
+ return files
73
+ except Exception as e:
74
+ print(f"[HFRepoTools] list_files failed: {e}")
75
+ return []
76
+
77
+ def repo_exists(self, repo_id: str) -> bool:
78
+ """Check if a repository exists."""
79
+ try:
80
+ self.api.repo_info(repo_id=repo_id, token=self.token)
81
+ return True
82
+ except:
83
+ return False
84
+
85
+ def create_repo_if_not_exists(
86
+ self,
87
+ repo_id: str,
88
+ repo_type: str = "model",
89
+ private: bool = False
90
+ ) -> bool:
91
+ """Create a repository if it doesn't exist."""
92
+ if self.repo_exists(repo_id):
93
+ return True
94
+ try:
95
+ create_repo(
96
+ repo_id=repo_id,
97
+ repo_type=repo_type,
98
+ private=private,
99
+ token=self.token
100
+ )
101
+ return True
102
+ except Exception as e:
103
+ print(f"[HFRepoTools] Failed to create repo: {e}")
104
+ return False
nexus_hf_mcp_bridge/mcp_bridge.py ADDED
@@ -0,0 +1,139 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ mcp_bridge.py
3
+ Custom HF MCP Bridge - Main Integration Layer
4
+
5
+ Exposes Hugging Face operations as MCP tools for Gradio Spaces.
6
+ This is the main entry point for the NEXUS Visual Weaver Space.
7
+ """
8
+
9
+ from typing import Dict, Any, List, Optional
10
+ from .hf_oauth import HFOAuthClient, authenticate_hf
11
+ from .token_manager import HFTokenManager
12
+ from .hf_repo_tools import HFRepoTools
13
+
14
+
15
+ class NEXUS_HF_MCP_Bridge:
16
+ """
17
+ Main bridge class that exposes HF capabilities as MCP tools.
18
+ Designed to be used inside a Gradio Space with mcp_server=True.
19
+ """
20
+
21
+ def __init__(self):
22
+ self.token_manager = HFTokenManager()
23
+ self.oauth_client = HFOAuthClient()
24
+ self.repo_tools: Optional[HFRepoTools] = None
25
+ self._authenticated = False
26
+
27
+ def authenticate(self) -> Dict[str, Any]:
28
+ """Start OAuth Device Code flow and store token."""
29
+ token = authenticate_hf()
30
+ if token:
31
+ self.token_manager.save_token(token)
32
+ self.repo_tools = HFRepoTools(token.access_token)
33
+ self._authenticated = True
34
+ return {"status": "success", "message": "Authenticated successfully"}
35
+ return {"status": "error", "message": "Authentication failed"}
36
+
37
+ def ensure_ready(self) -> bool:
38
+ """Make sure we have a valid token and repo_tools ready."""
39
+ if self._authenticated and self.repo_tools:
40
+ return True
41
+
42
+ token_str = self.token_manager.get_valid_token()
43
+ if token_str:
44
+ self.repo_tools = HFRepoTools(token_str)
45
+ self._authenticated = True
46
+ return True
47
+
48
+ return False
49
+
50
+ # ===================== MCP TOOLS =====================
51
+
52
+ def tool_authenticate(self) -> Dict[str, Any]:
53
+ """MCP Tool: Start Hugging Face OAuth authentication."""
54
+ return self.authenticate()
55
+
56
+ def tool_upload_file(
57
+ self,
58
+ local_path: str,
59
+ repo_id: str,
60
+ path_in_repo: str,
61
+ commit_message: Optional[str] = None
62
+ ) -> Dict[str, Any]:
63
+ """MCP Tool: Upload a file to a Hugging Face repository."""
64
+ if not self.ensure_ready():
65
+ return {"status": "error", "message": "Not authenticated. Call authenticate first."}
66
+
67
+ success = self.repo_tools.upload_file_to_repo(
68
+ local_path=local_path,
69
+ repo_id=repo_id,
70
+ path_in_repo=path_in_repo,
71
+ commit_message=commit_message
72
+ )
73
+ return {"status": "success" if success else "error"}
74
+
75
+ def tool_create_or_update_file(
76
+ self,
77
+ repo_id: str,
78
+ path_in_repo: str,
79
+ content: str,
80
+ commit_message: str = "Update via NEXUS HF MCP Bridge"
81
+ ) -> Dict[str, Any]:
82
+ """MCP Tool: Create or update a text file in a repo."""
83
+ if not self.ensure_ready():
84
+ return {"status": "error", "message": "Not authenticated."}
85
+
86
+ success = self.repo_tools.create_or_update_file(
87
+ repo_id=repo_id,
88
+ path_in_repo=path_in_repo,
89
+ content=content,
90
+ commit_message=commit_message
91
+ )
92
+ return {"status": "success" if success else "error"}
93
+
94
+ def tool_list_repo_files(self, repo_id: str) -> Dict[str, Any]:
95
+ """MCP Tool: List files in a repository."""
96
+ if not self.ensure_ready():
97
+ return {"status": "error", "message": "Not authenticated."}
98
+
99
+ files = self.repo_tools.list_files(repo_id=repo_id)
100
+ return {"status": "success", "files": files}
101
+
102
+ def tool_repo_exists(self, repo_id: str) -> Dict[str, Any]:
103
+ """MCP Tool: Check if a repository exists."""
104
+ if not self.ensure_ready():
105
+ return {"status": "error", "message": "Not authenticated."}
106
+
107
+ exists = self.repo_tools.repo_exists(repo_id=repo_id)
108
+ return {"status": "success", "exists": exists}
109
+
110
+
111
+ # Global bridge instance (for use in Gradio)
112
+ nexus_hf_bridge = NEXUS_HF_MCP_Bridge()
113
+
114
+
115
+ # Example of how to expose in Gradio app.py:
116
+ """
117
+ from mcp_bridge import nexus_hf_bridge
118
+
119
+ with gr.Blocks() as demo:
120
+ gr.Markdown("# NEXUS Visual Weaver - HF MCP Bridge")
121
+
122
+ with gr.Tab("HF Authentication"):
123
+ auth_btn = gr.Button("Authenticate with Hugging Face")
124
+ auth_output = gr.JSON()
125
+ auth_btn.click(nexus_hf_bridge.tool_authenticate, outputs=auth_output)
126
+
127
+ with gr.Tab("Repository Tools"):
128
+ repo_id = gr.Textbox(label="Repository ID (e.g. username/my-model)")
129
+ file_path = gr.Textbox(label="Path in repo")
130
+ content = gr.Textbox(label="Content", lines=10)
131
+ upload_btn = gr.Button("Create / Update File")
132
+ result = gr.JSON()
133
+
134
+ upload_btn.click(
135
+ nexus_hf_bridge.tool_create_or_update_file,
136
+ inputs=[repo_id, file_path, content],
137
+ outputs=result
138
+ )
139
+ """
nexus_hf_mcp_bridge/token_manager.py ADDED
@@ -0,0 +1,108 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ token_manager.py
3
+ Custom HF MCP Bridge - Token Management Layer
4
+
5
+ Handles secure storage and refresh of Hugging Face OAuth tokens
6
+ in a Space-friendly way (environment variables + optional file fallback).
7
+ """
8
+
9
+ import os
10
+ import json
11
+ import time
12
+ from typing import Optional
13
+ from datetime import datetime, timedelta
14
+ from .hf_oauth import HFOAuthToken, HFOAuthClient
15
+
16
+
17
+ class HFTokenManager:
18
+ """
19
+ Manages Hugging Face OAuth tokens with refresh support.
20
+ Designed to work well inside Hugging Face Spaces.
21
+ """
22
+
23
+ def __init__(self, token_file: str = "/tmp/hf_oauth_token.json"):
24
+ self.token_file = token_file
25
+ self._token: Optional[HFOAuthToken] = None
26
+ self._client = HFOAuthClient()
27
+
28
+ def load_token(self) -> Optional[HFOAuthToken]:
29
+ """Load token from environment or file."""
30
+ # Priority 1: Environment variable (recommended for Spaces)
31
+ env_token = os.environ.get("HF_OAUTH_ACCESS_TOKEN")
32
+ if env_token:
33
+ self._token = HFOAuthToken(access_token=env_token)
34
+ return self._token
35
+
36
+ # Priority 2: Token file
37
+ if os.path.exists(self.token_file):
38
+ try:
39
+ with open(self.token_file, "r") as f:
40
+ data = json.load(f)
41
+ self._token = HFOAuthToken(**data)
42
+ return self._token
43
+ except Exception as e:
44
+ print(f"[TokenManager] Failed to load token file: {e}")
45
+
46
+ return None
47
+
48
+ def save_token(self, token: HFOAuthToken) -> bool:
49
+ """Save token to file (and optionally env for current process)."""
50
+ self._token = token
51
+ try:
52
+ with open(self.token_file, "w") as f:
53
+ json.dump({
54
+ "access_token": token.access_token,
55
+ "token_type": token.token_type,
56
+ "expires_in": token.expires_in,
57
+ "refresh_token": token.refresh_token,
58
+ "scope": token.scope,
59
+ }, f, indent=2)
60
+ return True
61
+ except Exception as e:
62
+ print(f"[TokenManager] Failed to save token: {e}")
63
+ return False
64
+
65
+ def get_valid_token(self) -> Optional[str]:
66
+ """
67
+ Return a valid access token.
68
+ If expired and refresh_token exists, attempt refresh.
69
+ """
70
+ if not self._token:
71
+ self.load_token()
72
+
73
+ if not self._token:
74
+ return None
75
+
76
+ # For simplicity in Phase 1, we assume the token is still valid
77
+ # In production we would check expiry and refresh here.
78
+ return self._token.access_token
79
+
80
+ def ensure_authenticated(self) -> bool:
81
+ """
82
+ Ensure we have a valid token.
83
+ If not, start Device Code flow.
84
+ """
85
+ token = self.get_valid_token()
86
+ if token:
87
+ return True
88
+
89
+ print("[TokenManager] No valid token found. Starting OAuth flow...")
90
+ new_token = self._client.poll_for_token(
91
+ # This assumes the device flow was already started elsewhere
92
+ # In real usage we would combine with hf_oauth.start_device_flow()
93
+ device_code="" # Placeholder - real implementation would handle full flow
94
+ )
95
+
96
+ if new_token:
97
+ self.save_token(new_token)
98
+ return True
99
+ return False
100
+
101
+ def clear_token(self):
102
+ """Remove stored token."""
103
+ self._token = None
104
+ if os.path.exists(self.token_file):
105
+ try:
106
+ os.remove(self.token_file)
107
+ except:
108
+ pass