THE POINT: `explore_data_catalogue` is a full validate -> plan -> run pipeline that
re-authors the SQL from scratch on every call (~1-5 min, 20/hour). For a question
the user will ask AGAIN — a recurring check, a dashboard they watch, "my usual
semis screen" — save the tokens that exploration just produced. `run_saved_query`
then re-runs exactly those queries against live data in seconds, with no planning
step and no async job to poll. The backend ALSO authors a PLACEHOLDER twin of each
saved query (its ticker, CIK list, date, interval, and count lifted into typed
slots) plus a `metadata` slot schema naming them, so the same request can later be
re-asked for a different company, filer, or window:
`run_saved_query(query_placeholder, values={"symbol": "KSS"})`.
WHEN TO CALL: once an exploration is done and shown, offer it — say the request can
be re-run exactly or with different values — and save when the user says yes or
clearly implies they will want it again. Do NOT save every exploration by reflex,
and do not save a one-off.
Each query token is opaque ciphertext the server minted around the planned SQL.
NEVER write, guess, or edit one — a token this backend did not mint cannot be
decrypted and fails with 403 at replay time, long after the exploration is gone.
Owner-scoped: the row belongs to the calling user and is invisible to everyone
else. There is NO upsert (unlike `schedule_task`): saving the same request twice
creates a SECOND row. Check `list_saved_queries` first and `delete_saved_query` the
stale one rather than piling up near-duplicates.
Returns {"msg": "Successfully saved query: <user_request>", "id": <row id>,
"placeholder_status": "PENDING" | "NOT_DISPATCHED", "placeholder_id_to_poll"}. `id` is the delete key.
PENDING means the placeholder twins AND the row's `metadata` (its slot schema,
summary, and datasets) are being authored — roughly 10 s to 5 minutes, depending on
the size of the queries. Until they land, `query_placeholder` and `metadata` are
null in `list_saved_queries`, and an exact replay via `queries` works meanwhile.
NOT_DISPATCHED means no twins will be authored for this row: it can be replayed
exactly but not re-aimed.
Requires auth (your MCP client attaches the OAuth bearer automatically).
Rate-limited 100/min.