#!/usr/bin/env python3
"""Read-only diagnostic for Shafeh's MCP server (standard library only).

It initializes, sends notifications/initialized, lists tools, and calls the
`today` and `texts` tools with harmless read requests. It never writes.
Transport errors, protocol errors, tool errors and clarifications are reported
separately, so "empty" is never mistaken for "broken".

    python3 shafeh-mcp-check.py
    python3 shafeh-mcp-check.py --url http://localhost:3000/mcp
    python3 shafeh-mcp-check.py --credential-command "op read op://Private/Shafeh/token"

The token is read from SHAFEH_ACCESS_TOKEN or from --credential-command
(run as an argument vector, no shell). It is never printed.
"""
import argparse
import json
import os
import shlex
import subprocess
import sys
import urllib.error
import urllib.request

DEFAULT_URL = "https://shafeh.org/mcp"


def token_from(args):
    if args.credential_command:
        out = subprocess.run(shlex.split(args.credential_command), capture_output=True, text=True, check=True)
        return out.stdout.strip() or None
    return os.environ.get("SHAFEH_ACCESS_TOKEN") or None


def post(url, payload, token, agent):
    headers = {
        "Content-Type": "application/json",
        "Accept": "application/json, text/event-stream",
        "X-Agent-Name": agent,
    }
    if token:
        headers["Authorization"] = f"Bearer {token}"
    req = urllib.request.Request(url, data=json.dumps(payload).encode(), headers=headers, method="POST")
    try:
        with urllib.request.urlopen(req, timeout=30) as resp:
            return resp.status, resp.headers, resp.read().decode()
    except urllib.error.HTTPError as err:
        return err.code, err.headers, err.read().decode()


def parse(body):
    """JSON or a complete SSE response event, including multiline data."""
    body = body.strip()
    if not body:
        return None
    if body.startswith("{"):
        return json.loads(body)
    messages, data = [], []
    for line in (body.replace("\r\n", "\n") + "\n\n").splitlines():
        if not line:
            if data:
                messages.append(json.loads("\n".join(data)))
                data = []
        elif line.startswith("data:"):
            data.append(line[5:].removeprefix(" "))
    return next((message for message in reversed(messages) if "result" in message or "error" in message), None)


def main():
    ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
    ap.add_argument("--url", default=DEFAULT_URL)
    ap.add_argument("--agent-name", default="shafeh-mcp-check")
    ap.add_argument("--credential-command")
    ap.add_argument("--account", action="store_true", help="Also call learning/me to verify account scopes")
    args = ap.parse_args()
    token = token_from(args)
    ok = True

    def rpc(method, params=None, rid=1):
        payload = {"jsonrpc": "2.0", "method": method}
        if rid is not None:
            payload["id"] = rid
        if params is not None:
            payload["params"] = params
        return post(args.url, payload, token, args.agent_name)

    status, _, body = rpc("initialize", {"protocolVersion": "2025-06-18", "capabilities": {}, "clientInfo": {"name": args.agent_name, "version": "1"}})
    msg = parse(body)
    if status != 200 or not msg or "result" not in msg:
        print(f"✗ initialize: HTTP {status} {body[:200]}")
        return 1
    info = msg["result"].get("serverInfo", {})
    print(f"✓ initialize: {info.get('name')} {info.get('version')} ({'signed in' if token else 'anonymous'})")

    status, _, _ = rpc("notifications/initialized", rid=None)
    print(f"{'✓' if status in (200, 202) else '✗'} notifications/initialized: HTTP {status}")
    ok &= status in (200, 202)

    status, _, body = rpc("tools/list", rid=2)
    tools = [t["name"] for t in (parse(body) or {}).get("result", {}).get("tools", [])]
    print(f"{'✓' if tools else '✗'} tools/list: {', '.join(tools) or 'none'}")
    ok &= bool(tools)

    checks = [("today", "what's today"), ("texts", "Tehillim 23")]
    if args.account:
        checks.append(("learning_read" if "learning_read" in tools else "learning", "who am i"))
    for rid, (tool, request) in enumerate(checks, start=3):
        if tool not in tools:
            continue
        status, headers, body = rpc("tools/call", {"name": tool, "arguments": {"request": request}}, rid=rid)
        msg = parse(body) or {}
        if status in (401, 403):
            print(f"✗ {tool}: HTTP {status} — {headers.get('WWW-Authenticate', '')}")
            ok = False
        elif "error" in msg:
            print(f"✗ {tool}: JSON-RPC error {msg['error'].get('code')} {msg['error'].get('message')}")
            ok = False
        else:
            result = msg.get("result", {})
            data = result.get("structuredContent")
            if data is None:  # legacy text-only result: parse once
                try:
                    data = json.loads(result["content"][0]["text"])
                except (KeyError, IndexError, ValueError):
                    data = {}
            if result.get("isError"):
                print(f"✗ {tool}: tool error {data.get('error')}: {data.get('message')}")
                ok = False
            elif data.get("clarification_required"):
                print(f"? {tool}: clarification — {data.get('question')}")
            else:
                summary = data.get("ref") or (data.get("hebrew_date") or {}).get("english") or ", ".join(list(data)[:4])
                print(f"✓ {tool} ({request!r}): {summary}")
    return 0 if ok else 1


if __name__ == "__main__":
    sys.exit(main())
