#!/usr/bin/env python3
"""CSCS MarketSphere — local MCP server (stdio). REAL, runnable, stdlib-only.

Exposes CSCS tools to any MCP-capable AI agent (Claude Desktop, copilots). Each tool call goes to the
CSCS MarketSphere API with your API key, so it is authenticated and METERED (credits debited) by the
same billing spine as REST — no separate billing. Sandbox-only until the CSCS production licence lands.

Claude Desktop config (claude_desktop_config.json):
  {
    "mcpServers": {
      "cscs-marketsphere": {
        "command": "python",
        "args": ["/path/to/mcp_server.py"],
        "env": {
          "CSCS_MCP_API_KEY": "cscs_sk_sandbox_...",
          "CSCS_MCP_BASE": "https://cscs-marketsphere-platform.agreeablewave-1ac3ce0c.ukwest.azurecontainerapps.io"
        }
      }
    }
  }

Implements MCP JSON-RPC 2.0 over stdio: initialize, tools/list, tools/call. No third-party deps.
"""
import os
import sys
import json
import urllib.request

BASE = os.environ.get("CSCS_MCP_BASE", "https://cscs-marketsphere-platform.agreeablewave-1ac3ce0c.ukwest.azurecontainerapps.io").rstrip("/")
KEY = os.environ.get("CSCS_MCP_API_KEY", "")

# tool name -> (api path template, needs_symbol, one-line description)
TOOLS = {
    "cscs_securities":  ("/api/v1/securities", False, "Priced securities master (sandbox)."),
    "cscs_prices":      ("/api/v1/prices", False, "Latest prices for all securities."),
    "cscs_symbols":     ("/api/v1/symbols", False, "Symbol directory (code + name)."),
    "cscs_ownership":   ("/api/v1/ownership-summary", False, "Beneficial-ownership / concentration summary (depository-only)."),
    "cscs_ask_simba":   ("/api/v1/ask?q={q}", False, "Grounded natural-language Q&A over CSCS data. Arg: q (question)."),
    "cscs_news_intel":  ("/api/v1/news-intel/{symbol}", True, "AI news-intelligence for a security. Arg: symbol."),
    "cscs_stock360":    ("/api/v1/stock/360/{symbol}", True, "360° single-stock analysis (PREMIUM): decoded identity + "
                         "quant + ownership + grounded research + plain-English verdict. Full report on Standard+ keys; "
                         "a glimpse + upgrade prompt on free/developer keys (RBAC + credits enforced by your key). "
                         "Args: symbol; optional focus (comma list of market,quant,ownership,sentiment,research) and "
                         "depth (standard|deep) to steer the report."),
    "cscs_sentinel":    ("/api/v1/sentinel", False, "Market-integrity radar (flag-for-review)."),
}


def _call_api(path: str) -> dict:
    req = urllib.request.Request(BASE + path, headers={"X-API-Key": KEY, "Accept": "application/json"})
    try:
        with urllib.request.urlopen(req, timeout=30) as r:
            return json.loads(r.read().decode())
    except urllib.error.HTTPError as e:
        return {"error": f"HTTP {e.code}", "detail": e.read().decode()[:200]}
    except Exception as e:
        return {"error": str(e)}


def _tools_list() -> list:
    out = []
    for name, (path, needs_sym, desc) in TOOLS.items():
        props, required = {}, []
        if "{q}" in path:
            props["q"] = {"type": "string", "description": "The question to ask"}; required.append("q")
        if needs_sym:
            props["symbol"] = {"type": "string", "description": "Security symbol"}; required.append("symbol")
        if name == "cscs_stock360":
            props["focus"] = {"type": "string", "description": "Optional: comma list of sections to focus "
                              "(market,quant,ownership,sentiment,research). Full-tier keys only."}
            props["depth"] = {"type": "string", "enum": ["standard", "deep"], "description": "Report depth."}
        out.append({"name": name, "description": desc,
                    "inputSchema": {"type": "object", "properties": props, "required": required}})
    return out


def _tools_call(name: str, args: dict) -> dict:
    if name not in TOOLS:
        return {"content": [{"type": "text", "text": f"Unknown tool: {name}"}], "isError": True}
    if not KEY:
        return {"content": [{"type": "text", "text": "No API key. Set CSCS_MCP_API_KEY (get a sandbox key from the CSCS developer portal)."}], "isError": True}
    path, needs_sym, _ = TOOLS[name]
    if "{q}" in path:
        q = urllib.request.quote((args or {}).get("q", ""))
        path = path.format(q=q)
    if needs_sym:
        path = path.format(symbol=urllib.request.quote((args or {}).get("symbol", "")))
    if name == "cscs_stock360":
        qs = []
        if (args or {}).get("focus"):
            qs.append("focus=" + urllib.request.quote(args["focus"]))
        if (args or {}).get("depth"):
            qs.append("depth=" + urllib.request.quote(args["depth"]))
        if qs:
            path += ("&" if "?" in path else "?") + "&".join(qs)
    data = _call_api(path)
    return {"content": [{"type": "text", "text": json.dumps(data, indent=2)[:8000]}]}


def _send(obj: dict):
    sys.stdout.write(json.dumps(obj) + "\n")
    sys.stdout.flush()


def main():
    for line in sys.stdin:
        line = line.strip()
        if not line:
            continue
        try:
            msg = json.loads(line)
        except Exception:
            continue
        mid = msg.get("id")
        method = msg.get("method")
        if method == "initialize":
            _send({"jsonrpc": "2.0", "id": mid, "result": {
                "protocolVersion": "2024-11-05",
                "serverInfo": {"name": "cscs-marketsphere", "version": "0.1.0"},
                "capabilities": {"tools": {}}}})
        elif method == "tools/list":
            _send({"jsonrpc": "2.0", "id": mid, "result": {"tools": _tools_list()}})
        elif method == "tools/call":
            p = msg.get("params", {})
            _send({"jsonrpc": "2.0", "id": mid, "result": _tools_call(p.get("name"), p.get("arguments", {}))})
        elif method in ("notifications/initialized", "notifications/cancelled"):
            pass  # notifications: no response
        elif mid is not None:
            _send({"jsonrpc": "2.0", "id": mid, "error": {"code": -32601, "message": f"Method not found: {method}"}})


if __name__ == "__main__":
    main()
