diff --git a/scripts/mcp_gitea_smoke.py b/scripts/mcp_gitea_smoke.py new file mode 100644 index 0000000..0906ead --- /dev/null +++ b/scripts/mcp_gitea_smoke.py @@ -0,0 +1,85 @@ +#!/usr/bin/env python3 + +import json +import subprocess +import sys +import time + + +def send(proc: subprocess.Popen[bytes], payload: dict) -> None: + proc.stdin.write((json.dumps(payload) + "\n").encode("utf-8")) + proc.stdin.flush() + + +def read_line(proc: subprocess.Popen[bytes], timeout: float = 10.0) -> str: + start = time.time() + while time.time() - start < timeout: + line = proc.stdout.readline() + if line: + return line.decode("utf-8", errors="replace").strip() + time.sleep(0.05) + return "" + + +def main() -> int: + proc = subprocess.Popen( + ["python3", "scripts/run-gitea-mcp.py"], + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) + + try: + send( + proc, + { + "jsonrpc": "2.0", + "id": 1, + "method": "initialize", + "params": { + "protocolVersion": "2024-11-05", + "capabilities": {}, + "clientInfo": {"name": "smoke", "version": "0.0.0"}, + }, + }, + ) + init_resp = read_line(proc, timeout=15.0) + if not init_resp: + err = proc.stderr.read(4096).decode("utf-8", errors="replace") + sys.stderr.write("No initialize response. stderr:\n" + err + "\n") + return 1 + + print("initialize:", init_resp[:400]) + + send(proc, {"jsonrpc": "2.0", "id": 2, "method": "tools/list", "params": {}}) + tools_resp = read_line(proc, timeout=15.0) + if not tools_resp: + err = proc.stderr.read(4096).decode("utf-8", errors="replace") + sys.stderr.write("No tools/list response. stderr:\n" + err + "\n") + return 1 + + print("tools/list:", tools_resp[:400]) + + send( + proc, + { + "jsonrpc": "2.0", + "id": 3, + "method": "tools/call", + "params": {"name": "get_my_user_info", "arguments": {}}, + }, + ) + me_resp = read_line(proc, timeout=20.0) + if not me_resp: + err = proc.stderr.read(4096).decode("utf-8", errors="replace") + sys.stderr.write("No get_my_user_info response. stderr:\n" + err + "\n") + return 1 + + print("get_my_user_info:", me_resp[:500]) + return 0 + finally: + proc.terminate() + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/scripts/run-gitea-mcp.py b/scripts/run-gitea-mcp.py new file mode 100644 index 0000000..302ed88 --- /dev/null +++ b/scripts/run-gitea-mcp.py @@ -0,0 +1,87 @@ +#!/usr/bin/env python3 + +import os +import subprocess +import sys +from pathlib import Path + + +def load_dotenv(path: Path) -> dict[str, str]: + env: dict[str, str] = {} + + if not path.exists(): + return env + + for raw in path.read_text(encoding="utf-8").splitlines(): + line = raw.strip() + if not line or line.startswith("#"): + continue + if "=" not in line: + continue + + key, value = line.split("=", 1) + key = key.strip() + value = value.strip() + + if value and value[0] == "'" and value.endswith("'"): + value = value[1:-1] + elif value and value[0] == '"' and value.endswith('"'): + value = value[1:-1] + + env[key] = value + + return env + + +def main() -> int: + repo_root = Path(__file__).resolve().parents[1] + dotenv_path = repo_root / ".env" + + dotenv = load_dotenv(dotenv_path) + + required = ["GITEA_HOST", "GITEA_ACCESS_TOKEN"] + missing = [k for k in required if not dotenv.get(k)] + if missing: + sys.stderr.write( + "Missing required env vars in .env: " + ", ".join(missing) + "\n" + ) + return 2 + + env = os.environ.copy() + + # Prefer values from .env to keep VS Code config simple. + for key in [ + "GITEA_HOST", + "GITEA_ACCESS_TOKEN", + "GITEA_DEBUG", + "GITEA_READONLY", + "GITEA_INSECURE", + ]: + if key in dotenv: + env[key] = dotenv[key] + + cmd = [ + "docker", + "run", + "-i", + "--rm", + "-e", + "GITEA_HOST", + "-e", + "GITEA_ACCESS_TOKEN", + ] + + # Optional flags. + for optional in ["GITEA_DEBUG", "GITEA_READONLY", "GITEA_INSECURE"]: + if env.get(optional): + cmd.extend(["-e", optional]) + + cmd.append("docker.gitea.com/gitea-mcp-server:latest") + + # Inherit stdin/stdout/stderr for MCP stdio. + completed = subprocess.run(cmd, env=env, check=False) + return int(completed.returncode) + + +if __name__ == "__main__": + raise SystemExit(main())