import express from "express"; import { migrate, pool, seedSources } from "./db.js"; import { protectedResourceMetadata, requireMcpAuth } from "./auth.js"; import { handleMcp } from "./mcp.js"; import { SOURCES } from "./sources.js"; import { syncAll } from "./sync.js"; const app = express(); app.set("trust proxy", true); app.use(express.json({ limit: "2mb" })); app.get("/health", async (_req, res) => { try { await pool.query("SELECT 1"); res.json({ ok: true, service: "media-intelligence", version: "0.1.0" }); } catch (error) { res.status(503).json({ ok: false, error: error instanceof Error ? error.message : String(error) }); } }); app.get("/ready", async (_req, res) => { try { const result = await pool.query( `SELECT s.id, s.name, s.last_synced_at, s.last_error, COUNT(sig.id)::int AS signal_count FROM sources s LEFT JOIN signals sig ON sig.source_id=s.id GROUP BY s.id,s.name,s.last_synced_at,s.last_error ORDER BY s.id` ); const missing = result.rows .filter((row: any) => Number(row.signal_count) < 1) .map((row: any) => row.id); const errored = result.rows .filter((row: any) => row.last_error) .map((row: any) => row.id); if (missing.length || errored.length) { return res.status(503).json({ ok: false, missing_sources: missing, errored_sources: errored, sources: result.rows }); } return res.json({ ok: true, sources: result.rows }); } catch (error) { return res.status(503).json({ ok: false, error: error instanceof Error ? error.message : String(error) }); } }); app.get("/.well-known/oauth-protected-resource", (req, res) => res.json(protectedResourceMetadata(req))); app.get("/.well-known/oauth-protected-resource/api/mcp", (req, res) => res.json(protectedResourceMetadata(req))); app.post("/api/mcp", requireMcpAuth, handleMcp); app.get("/api/mcp", requireMcpAuth, (_req, res) => { res.status(405).setHeader("Allow", "POST").json({ error: "Use POST for MCP JSON-RPC requests" }); }); app.get("/", async (_req, res) => { const sourceRows = await pool.query("SELECT id,name,last_synced_at,last_error FROM sources ORDER BY id"); const counts = await pool.query(` SELECT (SELECT COUNT(*) FROM signals) AS signals, (SELECT COUNT(*) FROM opportunities) AS opportunities, (SELECT COUNT(*) FROM opportunities WHERE status='new') AS new_opportunities `); const c = counts.rows[0] ?? {}; res.type("html").send(`
Human-led NZ public-source monitoring behind a dedicated MCP.
${s.id}MCP endpoint: /api/mcp