from __future__ import annotations from fastapi import FastAPI from mcp.server.fastmcp import FastMCP from mcp.server.transport_security import TransportSecuritySettings from news_mcp.config import CLUSTERS_TTL_HOURS, DEFAULT_TOPICS, DB_PATH from news_mcp.jobs.poller import refresh_clusters from news_mcp.storage.sqlite_store import SQLiteClusterStore mcp = FastMCP( "news-mcp", transport_security=TransportSecuritySettings(enable_dns_rebinding_protection=False), ) @mcp.tool(description="What is happening right now? Return the latest deduplicated news clusters for a topic.") async def get_latest_events(topic: str = "crypto", limit: int = 5): limit = max(1, min(int(limit), 20)) # Refresh opportunistically (v1 simple: refresh every call but bounded to small RSS pull) refresh_clusters(topic=topic, limit=50) store = SQLiteClusterStore(DB_PATH) clusters = store.get_latest_clusters(topic=topic, ttl_hours=CLUSTERS_TTL_HOURS, limit=limit) # Ensure the response is compact and agent-friendly. out = [] for c in clusters: out.append( { "cluster_id": c.get("cluster_id"), "headline": c.get("headline"), "summary": c.get("summary"), "entities": c.get("entities", []), "sentiment": c.get("sentiment", "neutral"), "importance": c.get("importance", 0.0), "sources": c.get("sources", []), "timestamp": c.get("timestamp"), } ) return out app = FastAPI(title="News MCP Server") app.mount("/mcp", mcp.sse_app()) @app.get("/") def root(): return {"status": "ok", "transport": "fastmcp+sse", "mount": "/mcp", "tools": ["get_latest_events"]} @app.get("/health") def health(): return {"status": "ok", "ttl_hours": CLUSTERS_TTL_HOURS, "db": str(DB_PATH)}