Provide a signal-extraction MCP server that converts RSS into deduplicated, enriched news clusters that are easy for agents to use.
/mcpNEWS_EMBEDDINGS_ENABLED=true)NEWS_EMBEDDING_SIMILARITY_THRESHOLD)get_latest_events(topic, limit, include_articles)get_events_for_entity(entity, limit, timeframe, include_articles)get_event_summary(event_id, include_articles)detect_emerging_topics(limit)get_news_sentiment(entity, timeframe)get_related_recent_entities(subject, timeframe, limit, include_trends)get_capabilities()Instead of treating detect_emerging_topics() as a flat list, we want a higher-level representation:
Eventual agent tool shape (later): get_emerging_entity_graph(timeframe, limit).
NEWS_REFRESH_INTERVAL_SECONDS (default 900s)NEWS_CLUSTERS_TTL_HOURS via CLUSTERS_TTL_HOURS)get_event_summary/api/v1/*) for programmatic access to cluster data, sentiment series, entity frequencies, and health stats/dashboard — HTMX-based shell with Chart.js visualizations (5 views: health, clusters, sentiment, entities, detail)@app.on_event("startup") pruning to lifespan-based fire-and-forget background loop; server responds within ~0.3s regardless of feed/LLM latencynews-mcp/
├── news_mcp/mcp_server_fastmcp.py ← MCP tools + REST API + dashboard mount
├── news_mcp/dashboard/
│ ├── dashboard_store.py ← Read-only query layer (no side effects)
│ ├── index.html ← SPA shell with 5 views
│ ├── style.css ← Dark theme, responsive
│ └── dashboard.js ← Client-side rendering + Chart.js
SQLiteClusterStore with thin read-only methods — no enrichment, no writes_shared_store) avoids repeated DB connectionsStaticFiles mount — no Jinja2/templating dependencyfetch() + Chart.js avoids HTMX raw-JSON-in-DOM issuesNEWS_DEFAULT_LOOKBACK_HOURS (144h), not a hardcoded 24h/api/v1/*) — JSON-only, for programmatic access and the dashboard/dashboard — 5 views (health, clusters, sentiment, entities, detail), Chart.js visualizations, instant client-side rendering@app.on_event("startup") with lifespan-based fire-and-forget background loop; server responds in <0.3sasyncio.Lock prevents overlapping refresh cyclesnews-mcp/
├── news_mcp/mcp_server_fastmcp.py ← MCP + REST API + /dashboard static mount
├── news_mcp/dashboard/
│ ├── __init__.py
│ ├── dashboard_store.py ← Read-only query layer (no side effects)
│ ├── index.html ← SPA shell, 5 views
│ ├── style.css ← Dark theme, responsive grid
│ └── dashboard.js ← Client render, Chart.js, null-safe DOM access
SQLiteClusterStore with read-only methods (stats, pagination, series, frequencies, detail). No writes, no enrichment._shared_store) — one DB connection pool for the entire process.StaticFiles — no Jinja2/templating dependency.fetch() + Chart.js — avoids HTMX raw-JSON-in-DOM issues.NEWS_DEFAULT_LOOKBACK_HOURS (144h), not hardcoded.ORDER BY updated_at DESC + client-side sort as safety net).