#!/usr/bin/env python3 """ test-kilo-mcp-integration.py Тест интеграции MCP через mcp_settings.json (legacy Kilo Code path). Проверяет конечную цепочку: mcp_settings.json → stdio bridge → forgejo-mcp → Gitea API """ import json import os import subprocess import sys MCP_SETTINGS_PATH = os.path.expanduser( "~/.config/Code/User/globalStorage/kilocode.kilo-code/settings/mcp_settings.json" ) STDIO_SCRIPT = "/home/swp/Projects/APAW/scripts/mcp-gitea-stdio.cjs" def load_settings(): if not os.path.exists(MCP_SETTINGS_PATH): raise FileNotFoundError(f"MCP settings not found: {MCP_SETTINGS_PATH}") with open(MCP_SETTINGS_PATH) as f: return json.load(f) def validate_settings(data): assert "mcpServers" in data, "Missing mcpServers key" assert "forgejo-gitea" in data["mcpServers"], "Missing forgejo-gitea server" srv = data["mcpServers"]["forgejo-gitea"] assert "command" in srv, "Missing command" assert "args" in srv, "Missing args" assert "env" in srv, "Missing env" print(f"✅ Settings valid: command={srv['command']}, args={srv['args']}") return srv def test_stdio_rpc(server_config): print("\n[1] Initialize stdio bridge...") env = {**os.environ, **server_config.get("env", {})} cmd = [server_config["command"]] + server_config["args"] proc = subprocess.Popen( cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, env=env, cwd="/home/swp/Projects/APAW", ) def send(method, params=None, call_id=1): req = json.dumps({"jsonrpc": "2.0", "method": method, "params": params or {}, "id": call_id}) + "\n" proc.stdin.write(req) proc.stdin.flush() def recv(): line = proc.stdout.readline() return json.loads(line) if line.strip() else None # initialize send("initialize", { "protocolVersion": "2024-11-05", "capabilities": {}, "clientInfo": {"name": "test-kilo-mcp", "version": "1.0"} }, 1) resp = recv() assert resp and "result" in resp, f"Initialize failed: {resp}" print("✅ Initialize OK") # tools/list send("tools/list", {}, 2) resp = recv() tools = resp.get("result", {}).get("tools", []) assert len(tools) > 50, f"Expected >50 tools, got {len(tools)}" print(f"✅ Tools: {len(tools)}") # get_issue #110 print("\n[2] Call get_issue #110...") send("tools/call", { "name": "get_issue", "arguments": {"owner": "UniqueSoft", "repo": "APAW", "index": 110} }, 3) resp = recv() print(f" Raw resp keys: {list(resp.keys())}") content_arr = resp.get("result", {}).get("content", []) print(f" Content array len: {len(content_arr)}") if content_arr: content_text = content_arr[0].get("text", "") print(f" Content text len: {len(content_text)}") if not content_text: # fallback: parse result directly content_text = json.dumps(resp.get("result", {})) else: content_text = json.dumps(resp.get("result", {})) assert content_text, "Empty content" issue = json.loads(content_text) if content_text.startswith("{") else {"raw": content_text} if "number" not in issue: # direct result without wrapper issue = resp.get("result", {}) assert issue.get("number") == 110, f"Unexpected issue: {issue}" print(f"✅ Issue #{issue['number']} - {issue.get('title', 'N/A')}") # checkpoint print("\n[3] Verify checkpoint in body...") assert "## GNS Checkpoint" in (issue.get("body") or ""), "No checkpoint" print("✅ Checkpoint present") # budget/depth check print("\n[4] Extract checkpoint YAML...") body = issue.get("body", "") import re match = re.search(r"```yaml\n(.*?)\n```", body, re.S) assert match, "No YAML block in issue body" yaml_block = match.group(1) assert "budget:" in yaml_block, "No budget in checkpoint" assert "depth:" in yaml_block, "No depth in checkpoint" print("✅ Budget and depth found in checkpoint") proc.stdin.close() proc.wait(timeout=5) print("✅ Stdio bridge closed cleanly") def main(): print("=" * 60) print("Kilo Code MCP Integration Test") print("=" * 60) print(f"Settings path: {MCP_SETTINGS_PATH}") try: data = load_settings() srv = validate_settings(data) test_stdio_rpc(srv) print("\n" + "=" * 60) print("✅ ALL KILO MCP INTEGRATION TESTS PASSED") print("=" * 60) return 0 except Exception as e: print(f"\n❌ FAILED: {e}") import traceback traceback.print_exc() return 1 if __name__ == "__main__": sys.exit(main())