Spaces:
Runtime error
Runtime error
| # app.py | |
| import os | |
| import requests | |
| import gradio as gr | |
| from typing import Optional | |
| SESSION = requests.Session() | |
| SESSION.headers.update({ | |
| "Accept": "application/vnd.github+json", | |
| "X-GitHub-Api-Version": "2022-11-28", | |
| "User-Agent": "gradio-mcp-github-issue" | |
| }) | |
| def create_issue( | |
| owner_repo: str, | |
| title: str, | |
| body: str, | |
| github_token: Optional[str] = None, | |
| request: gr.Request = None | |
| ) -> str: | |
| """ | |
| Create a GitHub Issue as the authenticated user. | |
| Args: | |
| owner_repo (str): 対象リポジトリ。形式は `owner/repo`。 | |
| title (str): 作成する Issue のタイトル。 | |
| body (str): Issue 本文(任意)。 | |
| github_token (str, optional): UI から渡す GitHub Token。デフォルトは空。 | |
| request (gr.Request, optional): HTTP リクエスト。Authorization ヘッダを含む。 | |
| Returns: | |
| str: 作成した Issue の URL。失敗時はエラーメッセージを返す。 | |
| """ | |
| # 1) Authorization ヘッダ | |
| auth = "" | |
| if request and request.headers.get("authorization"): | |
| auth = request.headers.get("authorization").strip() | |
| # 2) UI入力 | |
| token = "" | |
| if auth: | |
| token = auth.split(" ", 1)[1] if auth.lower().startswith("bearer ") else auth | |
| elif github_token: | |
| token = github_token.strip() | |
| elif os.getenv("GITHUB_TOKEN"): | |
| token = os.getenv("GITHUB_TOKEN").strip() | |
| if not token: | |
| raise gr.Error("Missing GITHUB_TOKEN. Authorization ヘッダ、UI入力、または環境変数で指定してください。") | |
| # --- ここから下は既存の GitHub API 呼び出し処理 --- | |
| url = f"https://api.github.com/repos/{owner_repo}/issues" | |
| headers = {"Authorization": f"Bearer {token}"} | |
| payload = {"title": title.strip(), "body": (body or "")} | |
| resp = SESSION.post(url, headers=headers, json=payload, timeout=20) | |
| if resp.status_code == 201: | |
| data = resp.json() | |
| return f"✅ Issue created: #{data.get('number')}\n{data.get('html_url')}" | |
| else: | |
| try: | |
| err = resp.json() | |
| except Exception: | |
| err = {"message": resp.text} | |
| if resp.status_code == 401: | |
| raise gr.Error("401 Unauthorized: トークンが無効か権限が不足しています。repo スコープ等を確認してください。") | |
| if resp.status_code == 404: | |
| raise gr.Error("404 Not Found: リポジトリが存在しない、または権限がありません。") | |
| raise gr.Error(f"GitHub API Error {resp.status_code}: {err.get('message', err)}") | |
| # --- Gradio UI(開発・検証用) --- | |
| examples = [ | |
| ["octocat/Hello-World", "Test from Gradio MCP", "Created via demo server.", ""], | |
| ["owner/repo", "日本語タイトルの例", "本文に改行もOK\n- bullet 1\n- bullet 2", ""], | |
| ["owner/repo", "Token from UI", "UI のトークン欄で渡すテスト", "ghp_xxx_or_fine_grained_token"], | |
| ] | |
| with gr.Blocks() as demo: | |
| gr.Markdown("### GitHub Issue Creator (Gradio MCP)") | |
| with gr.Row(): | |
| owner_repo = gr.Textbox(label="owner/repo", placeholder="octocat/Hello-World") | |
| title = gr.Textbox(label="title", placeholder="Issue title") | |
| body = gr.Textbox(label="body (optional)", lines=6, placeholder="Issue body (markdown supported)") | |
| github_token = gr.Textbox( | |
| label="GITHUB_TOKEN (optional – for UI testing)", | |
| type="password", | |
| placeholder="例: ghp_xxx もしくは Fine-grained token" | |
| ) | |
| out = gr.Textbox(label="Result", lines=3, show_copy_button=True) | |
| run = gr.Button("Create Issue") | |
| run.click( | |
| create_issue, | |
| inputs=[owner_repo, title, body, github_token], | |
| outputs=[out] | |
| ) | |
| gr.Examples( | |
| examples=examples, | |
| inputs=[owner_repo, title, body, github_token], | |
| outputs=[out], | |
| fn=create_issue, | |
| cache_examples=False, | |
| ) | |
| # MCP サーバを同時起動(Streamable HTTP を使用) | |
| demo.launch( | |
| mcp_server=True, # ← これで MCP エンドポイントが有効化されます | |
| server_name="0.0.0.0", | |
| server_port=int(os.getenv("PORT", "7860")), | |
| ) | |