#!/usr/bin/env python3
"""Agent Sightings MCP server (stdio, no dependencies).

Reads the static JSON at https://agentsightings.com/api/cases.json (or AGENT_SIGHTINGS_URL, which may be
an http(s) URL or a local file path) and exposes search_cases, get_case and list_topics.
"""
import json, os, sys, re, urllib.request

URL = os.environ.get("AGENT_SIGHTINGS_URL", "https://agentsightings.com/api/cases.json")
_cache = None

def load():
    global _cache
    if _cache is None:
        if URL.startswith(("http://", "https://")):
            req = urllib.request.Request(URL, headers={"User-Agent": "agent-sightings-mcp/1.0"})
            raw = urllib.request.urlopen(req, timeout=30).read().decode("utf-8")
        else:
            raw = open(URL, encoding="utf-8").read()
        _cache = json.loads(raw)["cases"]
    return _cache

def short(c):
    return {k: c[k] for k in ("id", "title", "agent", "topic", "source", "page") if k in c} | {"summary": c["summary"][:240]}

def search_cases(query="", topic="", agent="", limit=10):
    words = [w for w in re.findall(r"\w+", query.lower()) if w]
    out = []
    for c in load():
        if topic and c["topic"].lower() != topic.lower(): continue
        if agent and agent.lower() not in c["agent"].lower(): continue
        hay = (c["title"] + " " + c["summary"] + " " + c["agent"] + " " + c["topic"]).lower()
        score = sum(hay.count(w) + (3 if w in c["title"].lower() else 0) for w in words) if words else 1
        if score: out.append((score, c.get("interest", 0), c))
    out.sort(key=lambda t: (-t[0], -t[1]))
    return [short(c) for _, _, c in out[: max(1, min(int(limit), 25))]]

def get_case(id):
    for c in load():
        if c["id"] == id: return c
    return {"error": f"no case with id {id!r}"}

def list_topics():
    n = {}
    for c in load(): n[c["topic"]] = n.get(c["topic"], 0) + 1
    return [{"topic": t, "count": k} for t, k in sorted(n.items(), key=lambda kv: -kv[1])]

TOOLS = [
 {"name": "search_cases", "description": "Search real, sourced AI agent use cases by keyword, with optional topic and agent filters.",
  "inputSchema": {"type": "object", "properties": {"query": {"type": "string"}, "topic": {"type": "string"}, "agent": {"type": "string"}, "limit": {"type": "integer", "default": 10}}}},
 {"name": "get_case", "description": "Get one case by id, including its reusable prompt and source URL.",
  "inputSchema": {"type": "object", "properties": {"id": {"type": "string"}}, "required": ["id"]}},
 {"name": "list_topics", "description": "List topics and how many cases each has.", "inputSchema": {"type": "object", "properties": {}}},
]
FUNCS = {"search_cases": search_cases, "get_case": get_case, "list_topics": list_topics}

def handle(msg):
    m, i, p = msg.get("method"), msg.get("id"), msg.get("params") or {}
    if m == "initialize":
        return {"jsonrpc": "2.0", "id": i, "result": {"protocolVersion": p.get("protocolVersion", "2024-11-05"), "capabilities": {"tools": {}},
                "serverInfo": {"name": "agent-sightings", "version": "1.0.0"}}}
    if m == "tools/list":
        return {"jsonrpc": "2.0", "id": i, "result": {"tools": TOOLS}}
    if m == "tools/call":
        name, args = p.get("name"), p.get("arguments") or {}
        try:
            res = FUNCS[name](**args)
            return {"jsonrpc": "2.0", "id": i, "result": {"content": [{"type": "text", "text": json.dumps(res, ensure_ascii=False, indent=1)}]}}
        except Exception as ex:
            return {"jsonrpc": "2.0", "id": i, "result": {"isError": True, "content": [{"type": "text", "text": f"error: {ex}"}]}}
    if m == "ping":
        return {"jsonrpc": "2.0", "id": i, "result": {}}
    if i is not None:
        return {"jsonrpc": "2.0", "id": i, "error": {"code": -32601, "message": f"method not found: {m}"}}
    return None  # notifications

def main():
    for line in sys.stdin:
        line = line.strip()
        if not line: continue
        try: msg = json.loads(line)
        except ValueError: continue
        r = handle(msg)
        if r is not None:
            sys.stdout.write(json.dumps(r, ensure_ascii=False) + "\n"); sys.stdout.flush()

if __name__ == "__main__":
    main()
