Harness: runner, generator, tasks T01 T13 T14 T15, CLAUDE.md

This commit is contained in:
Kral
2026-10-02 21:42:51 +02:00
commit 1c6c85492e
54 changed files with 3732 additions and 0 deletions

96
harness/mcp_client.py Normal file
View File

@@ -0,0 +1,96 @@
"""Minimal MCP streamable-http client. One session per worker."""
import json
import os
import random
import time
import urllib.request
import urllib.error
class McpError(Exception):
pass
class McpClient:
def __init__(self, url=None, token=None, timeout=300):
self.url = url or os.environ.get("MCP_URL", "http://127.0.0.1:3000/mcp")
self.token = token or os.environ.get("MCP_TOKEN", "")
self.timeout = timeout
self.sid = None
self._id = 0
self._lock_retries = 0
self.lock_retry_total = 0
def _post(self, body, method="POST"):
h = {"Content-Type": "application/json",
"Accept": "application/json, text/event-stream"}
if self.token:
h["Authorization"] = f"Bearer {self.token}"
if self.sid:
h["Mcp-Session-Id"] = self.sid
data = json.dumps(body).encode() if body is not None else None
req = urllib.request.Request(self.url, data, h, method=method)
with urllib.request.urlopen(req, timeout=self.timeout) as r:
sid = r.headers.get("Mcp-Session-Id")
raw = r.read().decode()
if sid:
self.sid = sid
if "data:" in raw[:40]:
raw = "".join(l[5:].strip() for l in raw.splitlines() if l.startswith("data:"))
return json.loads(raw) if raw.strip() else None
def open(self):
self._id += 1
self._post({"jsonrpc": "2.0", "id": self._id, "method": "initialize",
"params": {"protocolVersion": "2025-03-26", "capabilities": {},
"clientInfo": {"name": "abap-llm-harness", "version": "0.1"}}})
self._post({"jsonrpc": "2.0", "method": "notifications/initialized"})
return self
def close(self):
if self.sid:
try:
self._post(None, method="DELETE")
except Exception:
pass
self.sid = None
def list_tools(self):
self._id += 1
res = self._post({"jsonrpc": "2.0", "id": self._id, "method": "tools/list"})
return res["result"]["tools"]
def call(self, name, args, retry=True):
"""Return (is_error, text)."""
self._id += 1
body = {"jsonrpc": "2.0", "id": self._id, "method": "tools/call",
"params": {"name": name, "arguments": args}}
try:
res = self._post(body)
except urllib.error.HTTPError as e:
if e.code == 404 and retry: # session dropped (capacity limit)
self.sid = None
self.open()
return self.call(name, args, retry=False)
raise McpError(f"{name}: HTTP {e.code}") from e
if "error" in res:
return True, json.dumps(res["error"])
r = res["result"]
text = "\n".join(c.get("text", "") for c in r.get("content", []))
# The server shares one RFC connection between sessions. A parallel call gets
# "Concurrent call detected" instead of waiting. Retry; the caller never sees it.
if "Concurrent call detected" in text and self._lock_retries < 8:
self._lock_retries += 1
time.sleep(0.5 * self._lock_retries + random.random())
try:
return self.call(name, args, retry)
finally:
self._lock_retries = 0
self.lock_retry_total += self._lock_retries
return bool(r.get("isError")), text
def __enter__(self):
return self.open()
def __exit__(self, *a):
self.close()