Spaces:
Configuration error
Configuration error
| """Trifecta-Bro MCP Server (MCP 2.0 / FastMCP-style).""" | |
| from __future__ import annotations | |
| import json | |
| from datetime import date as date_type, timedelta | |
| from pathlib import Path | |
| from typing import Any | |
| from mcp.server.mcpserver import MCPServer | |
| from mcp.types import TextContent | |
| mcp = MCPServer("trifecta-bro") | |
| REPO_ROOT = Path(__file__).resolve().parents[1] | |
| PREDICTIONS_DIR = REPO_ROOT / "data" / "predictions" | |
| PDF_DIR = REPO_ROOT / "data" / "pdfs" | |
| PREDICTIONS_DIR.mkdir(parents=True, exist_ok=True) | |
| PDF_DIR.mkdir(parents=True, exist_ok=True) | |
| def _predictions_path(for_date: str) -> Path: | |
| return PREDICTIONS_DIR / f"predictions-{for_date}.json" | |
| def _load_predictions(for_date: str) -> dict[str, Any]: | |
| p = _predictions_path(for_date) | |
| if not p.exists(): | |
| raise FileNotFoundError(f"No predictions for {for_date}") | |
| return json.loads(p.read_text()) | |
| def _pdfs() -> list[str]: | |
| if not PDF_DIR.exists(): | |
| return [] | |
| return sorted([p.name for p in PDF_DIR.glob("*.pdf")]) | |
| def get_predictions(for_date: str | None = None) -> list[TextContent]: | |
| """Return stored trifecta predictions for a date. | |
| If *for_date* is omitted, tomorrow's date (Australia/Adelaide) is used. | |
| """ | |
| target = for_date or (date_type.today() + timedelta(days=1)).isoformat() | |
| try: | |
| payload = _load_predictions(target) | |
| except FileNotFoundError: | |
| return [TextContent(type="text", text=json.dumps({ | |
| "error": f"No predictions for {target}", | |
| "date": target, | |
| "tip": "Run generate_predictions.py or run_daily_analysis() first.", | |
| }, indent=2))] | |
| payload["_meta"] = {"date": target, "server": "trifecta-bro-mcp"} | |
| return [TextContent(type="text", text=json.dumps(payload, indent=2))] | |
| def get_race(for_date: str, race_number: int) -> list[TextContent]: | |
| """Return a single race block from stored predictions.""" | |
| target = for_date | |
| try: | |
| payload = _load_predictions(target) | |
| except FileNotFoundError: | |
| return [TextContent(type="text", text=json.dumps({ | |
| "error": f"No predictions for {target}" | |
| }, indent=2))] | |
| for race in payload.get("races", []): | |
| if int(race.get("race_number", 0)) == int(race_number): | |
| pred = race.get("prediction", {}) | |
| out = { | |
| "date": target, | |
| "race_number": race.get("race_number"), | |
| "race_name": race.get("race_name"), | |
| "track": race.get("track"), | |
| "distance": race.get("distance"), | |
| "condition": race.get("condition"), | |
| "race_class": race.get("race_class"), | |
| "prize_money": race.get("prize_money"), | |
| "number_of_runners": race.get("number_of_runners"), | |
| "prediction": pred, | |
| } | |
| return [TextContent(type="text", text=json.dumps(out, indent=2))] | |
| return [TextContent(type="text", text=json.dumps({ | |
| "error": f"Race {race_number} not found for {target}" | |
| }, indent=2))] | |
| def list_pdfs() -> list[TextContent]: | |
| """List generated PDF report filenames.""" | |
| return [TextContent(type="text", text=json.dumps({"pdfs": _pdfs()}, indent=2))] | |
| def get_latest_meetings(for_date: str | None = None) -> list[TextContent]: | |
| """List available meeting dates with stored predictions.""" | |
| target = for_date or (date_type.today() + timedelta(days=1)).isoformat() | |
| dates = sorted([p.stem.replace("predictions-", "") for p in PREDICTIONS_DIR.glob("predictions-*.json")]) | |
| return [TextContent(type="text", text=json.dumps({ | |
| "date": target, | |
| "available_dates": dates, | |
| "count": len(dates), | |
| }, indent=2))] | |
| def health() -> list[TextContent]: | |
| """Server health and repo status.""" | |
| return [TextContent(type="text", text=json.dumps({ | |
| "server": "trifecta-bro-mcp", | |
| "status": "ok", | |
| "repo_root": str(REPO_ROOT), | |
| "predictions_dir": str(PREDICTIONS_DIR), | |
| "pdf_dir": str(PDF_DIR), | |
| "predictions_count": len(list(PREDICTIONS_DIR.glob("predictions-*.json"))) if PREDICTIONS_DIR.exists() else 0, | |
| "pdf_count": len(_pdfs()), | |
| }, indent=2))] | |
| if __name__ == "__main__": | |
| import asyncio | |
| asyncio.run(mcp.run_stdio_async()) | |