# 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")), )