137 lines
4.0 KiB
Python
Executable File
137 lines
4.0 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""
|
|
e2e-mcp-stdio-test.py
|
|
End-to-end test for MCP Gitea stdio transport.
|
|
|
|
1. Spawn stdio bridge via bun
|
|
2. Call initialize
|
|
3. Call tools/list
|
|
4. Call tools/call gitea_get_issue for issue #110
|
|
5. Validate response
|
|
6. Compare with REST API fallback
|
|
"""
|
|
|
|
import subprocess
|
|
import json
|
|
import sys
|
|
import time
|
|
|
|
STDIO_CMD = ["bun", "scripts/mcp-gitea-stdio.cjs"]
|
|
GITEA_API = "https://git.softuniq.eu/api/v1/repos/UniqueSoft/APAW"
|
|
USER, PASS = "NW", "eshkink0t"
|
|
|
|
def main():
|
|
print("="*60)
|
|
print("E2E MCP Stdio Test")
|
|
print("="*60)
|
|
|
|
proc = subprocess.Popen(
|
|
STDIO_CMD,
|
|
stdin=subprocess.PIPE,
|
|
stdout=subprocess.PIPE,
|
|
stderr=subprocess.PIPE,
|
|
text=True,
|
|
cwd="/home/swp/Projects/APAW",
|
|
)
|
|
|
|
def send(msg):
|
|
line = json.dumps(msg) + "\n"
|
|
proc.stdin.write(line)
|
|
proc.stdin.flush()
|
|
print(f"→ {line.strip()}")
|
|
|
|
def recv():
|
|
line = proc.stdout.readline()
|
|
print(f"← {line.strip()}")
|
|
return json.loads(line)
|
|
|
|
# 1. Initialize
|
|
print("\n[1] Initialize...")
|
|
send({
|
|
"jsonrpc": "2.0",
|
|
"method": "initialize",
|
|
"params": {
|
|
"protocolVersion": "2024-05-08",
|
|
"capabilities": {},
|
|
"clientInfo": {"name": "e2e-test-client", "version": "1.0.0"}
|
|
},
|
|
"id": 1
|
|
})
|
|
resp = recv()
|
|
assert resp["result"]["serverInfo"]["name"] == "forgejo-mcp", "Unexpected server name"
|
|
print("✅ Initialize OK")
|
|
|
|
# 2. tools/list
|
|
print("\n[2] List tools...")
|
|
send({"jsonrpc": "2.0", "method": "tools/list", "params": {}, "id": 2})
|
|
resp = recv()
|
|
tools = resp.get("result", {}).get("tools", [])
|
|
assert len(tools) > 50, f"Expected >50 tools, got {len(tools)}"
|
|
print(f"✅ Tools listed: {len(tools)}")
|
|
|
|
# 3. tools/call get_issue
|
|
print("\n[3] Call get_issue #110...")
|
|
send({
|
|
"jsonrpc": "2.0",
|
|
"method": "tools/call",
|
|
"params": {
|
|
"name": "get_issue",
|
|
"arguments": {
|
|
"owner": "UniqueSoft",
|
|
"repo": "APAW",
|
|
"index": 110
|
|
}
|
|
},
|
|
"id": 3
|
|
})
|
|
resp = recv()
|
|
print(f"DEBUG: Response received: {resp}")
|
|
result_content = resp["result"]["content"]
|
|
print(f"DEBUG: Result content: {result_content}")
|
|
result_text = result_content[0]["text"]
|
|
print(f"DEBUG: Result text: {result_text}")
|
|
issue_data = json.loads(result_text)
|
|
assert issue_data["number"] == 110, f"Expected issue 110, got {issue_data.get('number')}"
|
|
print(f"✅ Issue fetched: #{issue_data['number']} - {issue_data.get('title', 'N/A')}")
|
|
|
|
# 4. Verify checkpoint exists in issue body
|
|
print("\n[4] Verify checkpoint in issue body...")
|
|
assert "## GNS Checkpoint" in (issue_data.get("body") or ""), "Checkpoint not found in issue body"
|
|
print("✅ Checkpoint found")
|
|
|
|
# 5. Compare with REST API for consistency
|
|
print("\n[5] REST API consistency check...")
|
|
import urllib.request
|
|
import base64
|
|
creds = base64.b64encode(f"{USER}:{PASS}".encode()).decode()
|
|
req = urllib.request.Request(
|
|
f"{GITEA_API}/issues/110",
|
|
headers={"Accept": "application/json", "Authorization": f"Basic {creds}"}
|
|
)
|
|
with urllib.request.urlopen(req) as r:
|
|
rest_issue = json.loads(r.read())
|
|
assert rest_issue["number"] == issue_data["number"], "MCP and REST issue numbers differ"
|
|
assert rest_issue["title"] == issue_data["title"], "MCP and REST issue titles differ"
|
|
print("✅ REST API consistent")
|
|
|
|
# 6. Close gracefully
|
|
print("\n[6] Terminate stdio bridge...")
|
|
proc.stdin.close()
|
|
proc.wait(timeout=5)
|
|
print("✅ Stdio bridge closed")
|
|
|
|
print("\n" + "="*60)
|
|
print("✅ ALL E2E MCP STDIO TESTS PASSED")
|
|
print("="*60)
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
try:
|
|
sys.exit(main())
|
|
except Exception as e:
|
|
print(f"\n❌ FAILED: {e}")
|
|
import traceback
|
|
traceback.print_exc()
|
|
sys.exit(1)
|