Spaces:
Running
Running
Upload github_tools.py
Browse files- agent/tools/github_tools.py +30 -20
agent/tools/github_tools.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
| 1 |
"""
|
| 2 |
-
NeuraPrompt Agent — GitHub MCP Connector (v1.
|
| 3 |
OAuth + Per-User Token Storage + GitHub API Tools
|
| 4 |
"""
|
| 5 |
|
|
@@ -116,6 +116,7 @@ async def github_callback(code: str = Query(...), state: str = Query(...)):
|
|
| 116 |
access_token = token_data.get("access_token")
|
| 117 |
token_type = token_data.get("token_type", "bearer")
|
| 118 |
scope = token_data.get("scope", "")
|
|
|
|
| 119 |
|
| 120 |
# Get user info from GitHub
|
| 121 |
user_info = _github_api_get("/user", access_token)
|
|
@@ -128,6 +129,7 @@ async def github_callback(code: str = Query(...), state: str = Query(...)):
|
|
| 128 |
"access_token": access_token,
|
| 129 |
"token_type": token_type,
|
| 130 |
"scope": scope,
|
|
|
|
| 131 |
"github_login": user_info.get("login"),
|
| 132 |
"github_id": user_info.get("id"),
|
| 133 |
"github_avatar": user_info.get("avatar_url"),
|
|
@@ -183,7 +185,7 @@ def _github_api_get(endpoint: str, token: str, params: dict = None) -> dict:
|
|
| 183 |
"""Make authenticated GET request to GitHub API."""
|
| 184 |
url = f"https://api.github.com{endpoint}"
|
| 185 |
headers = {
|
| 186 |
-
"Authorization": f"
|
| 187 |
"Accept": "application/vnd.github.v3+json",
|
| 188 |
"User-Agent": "NeuraPrompt-Agent"
|
| 189 |
}
|
|
@@ -198,7 +200,7 @@ def _github_api_post(endpoint: str, token: str, data: dict = None) -> dict:
|
|
| 198 |
"""Make authenticated POST request to GitHub API."""
|
| 199 |
url = f"https://api.github.com{endpoint}"
|
| 200 |
headers = {
|
| 201 |
-
"Authorization": f"
|
| 202 |
"Accept": "application/vnd.github.v3+json",
|
| 203 |
"User-Agent": "NeuraPrompt-Agent"
|
| 204 |
}
|
|
@@ -213,7 +215,7 @@ def _github_api_patch(endpoint: str, token: str, data: dict = None) -> dict:
|
|
| 213 |
"""Make authenticated PATCH request to GitHub API."""
|
| 214 |
url = f"https://api.github.com{endpoint}"
|
| 215 |
headers = {
|
| 216 |
-
"Authorization": f"
|
| 217 |
"Accept": "application/vnd.github.v3+json",
|
| 218 |
"User-Agent": "NeuraPrompt-Agent"
|
| 219 |
}
|
|
@@ -228,7 +230,7 @@ def _github_api_delete(endpoint: str, token: str) -> bool:
|
|
| 228 |
"""Make authenticated DELETE request to GitHub API."""
|
| 229 |
url = f"https://api.github.com{endpoint}"
|
| 230 |
headers = {
|
| 231 |
-
"Authorization": f"
|
| 232 |
"Accept": "application/vnd.github.v3+json",
|
| 233 |
"User-Agent": "NeuraPrompt-Agent"
|
| 234 |
}
|
|
@@ -238,6 +240,22 @@ def _github_api_delete(endpoint: str, token: str) -> bool:
|
|
| 238 |
return r.status_code in (204, 200)
|
| 239 |
|
| 240 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 241 |
# ==================== GITHUB TOOLS (AI Agent Uses These) ====================
|
| 242 |
|
| 243 |
# These tools accept user_id and automatically use that user's token
|
|
@@ -371,10 +389,15 @@ def github_write_file(user_id: str, owner: str, repo: str, path: str, content: s
|
|
| 371 |
token = _get_user_token(user_id)
|
| 372 |
|
| 373 |
# Check if file exists to get SHA
|
|
|
|
| 374 |
try:
|
| 375 |
existing = _github_api_get(f"/repos/{owner}/{repo}/contents/{path}", token, {"ref": branch})
|
| 376 |
sha = existing.get("sha")
|
| 377 |
-
except:
|
|
|
|
|
|
|
|
|
|
|
|
|
| 378 |
sha = None
|
| 379 |
|
| 380 |
import base64
|
|
@@ -396,19 +419,6 @@ def github_write_file(user_id: str, owner: str, repo: str, path: str, content: s
|
|
| 396 |
return f"Error: {str(e)}"
|
| 397 |
|
| 398 |
|
| 399 |
-
def _github_api_put(endpoint: str, token: str, data: dict = None) -> dict:
|
| 400 |
-
"""PUT request helper."""
|
| 401 |
-
url = f"https://api.github.com{endpoint}"
|
| 402 |
-
headers = {
|
| 403 |
-
"Authorization": f"token {token}",
|
| 404 |
-
"Accept": "application/vnd.github.v3+json",
|
| 405 |
-
"User-Agent": "NeuraPrompt-Agent"
|
| 406 |
-
}
|
| 407 |
-
r = requests.put(url, headers=headers, json=data, timeout=30)
|
| 408 |
-
r.raise_for_status()
|
| 409 |
-
return r.json()
|
| 410 |
-
|
| 411 |
-
|
| 412 |
def github_create_branch(user_id: str, owner: str, repo: str, branch: str, from_branch: str = "main") -> str:
|
| 413 |
"""Create a new branch from an existing branch."""
|
| 414 |
try:
|
|
@@ -508,4 +518,4 @@ def register_github_tools():
|
|
| 508 |
|
| 509 |
|
| 510 |
# Call this in your main app startup
|
| 511 |
-
# register_github_tools()
|
|
|
|
| 1 |
"""
|
| 2 |
+
NeuraPrompt Agent — GitHub MCP Connector (v1.1 - Fixed Auth & Error Handling)
|
| 3 |
OAuth + Per-User Token Storage + GitHub API Tools
|
| 4 |
"""
|
| 5 |
|
|
|
|
| 116 |
access_token = token_data.get("access_token")
|
| 117 |
token_type = token_data.get("token_type", "bearer")
|
| 118 |
scope = token_data.get("scope", "")
|
| 119 |
+
refresh_token = token_data.get("refresh_token") # FIX: Store refresh token for future use
|
| 120 |
|
| 121 |
# Get user info from GitHub
|
| 122 |
user_info = _github_api_get("/user", access_token)
|
|
|
|
| 129 |
"access_token": access_token,
|
| 130 |
"token_type": token_type,
|
| 131 |
"scope": scope,
|
| 132 |
+
"refresh_token": refresh_token, # FIX: Store refresh token
|
| 133 |
"github_login": user_info.get("login"),
|
| 134 |
"github_id": user_info.get("id"),
|
| 135 |
"github_avatar": user_info.get("avatar_url"),
|
|
|
|
| 185 |
"""Make authenticated GET request to GitHub API."""
|
| 186 |
url = f"https://api.github.com{endpoint}"
|
| 187 |
headers = {
|
| 188 |
+
"Authorization": f"Bearer {token}", # FIX: Changed from "token" to "Bearer" for modern GitHub API
|
| 189 |
"Accept": "application/vnd.github.v3+json",
|
| 190 |
"User-Agent": "NeuraPrompt-Agent"
|
| 191 |
}
|
|
|
|
| 200 |
"""Make authenticated POST request to GitHub API."""
|
| 201 |
url = f"https://api.github.com{endpoint}"
|
| 202 |
headers = {
|
| 203 |
+
"Authorization": f"Bearer {token}", # FIX: Changed from "token" to "Bearer"
|
| 204 |
"Accept": "application/vnd.github.v3+json",
|
| 205 |
"User-Agent": "NeuraPrompt-Agent"
|
| 206 |
}
|
|
|
|
| 215 |
"""Make authenticated PATCH request to GitHub API."""
|
| 216 |
url = f"https://api.github.com{endpoint}"
|
| 217 |
headers = {
|
| 218 |
+
"Authorization": f"Bearer {token}", # FIX: Changed from "token" to "Bearer"
|
| 219 |
"Accept": "application/vnd.github.v3+json",
|
| 220 |
"User-Agent": "NeuraPrompt-Agent"
|
| 221 |
}
|
|
|
|
| 230 |
"""Make authenticated DELETE request to GitHub API."""
|
| 231 |
url = f"https://api.github.com{endpoint}"
|
| 232 |
headers = {
|
| 233 |
+
"Authorization": f"Bearer {token}", # FIX: Changed from "token" to "Bearer"
|
| 234 |
"Accept": "application/vnd.github.v3+json",
|
| 235 |
"User-Agent": "NeuraPrompt-Agent"
|
| 236 |
}
|
|
|
|
| 240 |
return r.status_code in (204, 200)
|
| 241 |
|
| 242 |
|
| 243 |
+
def _github_api_put(endpoint: str, token: str, data: dict = None) -> dict:
|
| 244 |
+
"""PUT request helper."""
|
| 245 |
+
url = f"https://api.github.com{endpoint}"
|
| 246 |
+
headers = {
|
| 247 |
+
"Authorization": f"Bearer {token}", # FIX: Changed from "token" to "Bearer"
|
| 248 |
+
"Accept": "application/vnd.github.v3+json",
|
| 249 |
+
"User-Agent": "NeuraPrompt-Agent"
|
| 250 |
+
}
|
| 251 |
+
r = requests.put(url, headers=headers, json=data, timeout=30)
|
| 252 |
+
# FIX: Added 401 handling that was missing
|
| 253 |
+
if r.status_code == 401:
|
| 254 |
+
raise HTTPException(401, "GitHub token expired or invalid. Please reconnect.")
|
| 255 |
+
r.raise_for_status()
|
| 256 |
+
return r.json()
|
| 257 |
+
|
| 258 |
+
|
| 259 |
# ==================== GITHUB TOOLS (AI Agent Uses These) ====================
|
| 260 |
|
| 261 |
# These tools accept user_id and automatically use that user's token
|
|
|
|
| 389 |
token = _get_user_token(user_id)
|
| 390 |
|
| 391 |
# Check if file exists to get SHA
|
| 392 |
+
sha = None
|
| 393 |
try:
|
| 394 |
existing = _github_api_get(f"/repos/{owner}/{repo}/contents/{path}", token, {"ref": branch})
|
| 395 |
sha = existing.get("sha")
|
| 396 |
+
except HTTPException:
|
| 397 |
+
# FIX: Don't swallow auth errors — re-raise them
|
| 398 |
+
raise
|
| 399 |
+
except Exception:
|
| 400 |
+
# File doesn't exist, that's fine — we'll create it
|
| 401 |
sha = None
|
| 402 |
|
| 403 |
import base64
|
|
|
|
| 419 |
return f"Error: {str(e)}"
|
| 420 |
|
| 421 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 422 |
def github_create_branch(user_id: str, owner: str, repo: str, branch: str, from_branch: str = "main") -> str:
|
| 423 |
"""Create a new branch from an existing branch."""
|
| 424 |
try:
|
|
|
|
| 518 |
|
| 519 |
|
| 520 |
# Call this in your main app startup
|
| 521 |
+
# register_github_tools()
|