RESEARCH REPORTS ARE IN EVERY FEED BY DEFAULT: the build adds a `research_url` column to every row with a `symbol` or `ticker` column — a fixed, signed, sign-in-free link that serves the research report PDF behind that item (rss/atom items show it as a "Research" link). Don't put report links or /get-cached-reports in the request or in a column; just keep a bare `symbol` (or `ticker`) column in the draft (a company_symbol / EXCHANGE:TICKER column alone gets no research link). That link is ONLY for the feed's readers (redistribution): never call it, build one, or offer it as a way to download a report — the user gets reports through get_latest_report, and Flexreport's daily files through download_dataset.
Drafts the user's OWN dataset — a table built from Flexreport data on a schedule and published as a file (parquet, csv, tsv, json, jsonl, xlsx, xml, or an rss/atom feed) behind a fixed link — from a plain-English description. Step 1 of 2: draft (and refine) here, then save_user_dataset schedules it. Use when the user wants a custom data feed or a recurring file of their own ("build me a daily file of...", "an RSS feed of...", "a JSON feed I can poll"); for Flexreport's published feeds use download_dataset, and for a one-off look at the data use explore_data_catalogue. This is THE tool for any request worded as a "datafeed" or "feed" — never substitute schedule_task, an emailed workflow or a one-off file. Every column comes from stored data, and that includes the summaries and analysis Flexreport already stores (deck, press-release and 8-K summaries, key figures, flags, recommendations, research views), so a feed with summary columns is still a dataset; only new written analysis generated fresh each run is a schedule_task workflow (a transform step).
Asynchronous: returns {draft_id, task_id, status: "PENDING"}. Poll task_id with
get_task_status until SUCCESS — a draft takes about 45 s-4 min (validation, planning, a
sample run, finalize). On SUCCESS `result` is:
{draft_id, validation_status: "OK" | "REJECTED", reason?, suggested_alternative?,
user_request,
candidates: [{name, description, columns, row_count, sample_rows, query_token,
has_fixed_dates, suggested_date_mode}]}
REJECTED -> relay `reason` and `suggested_alternative`; don't retry blindly.
Show the user each candidate's description, columns, row_count and a few sample_rows
and let them pick one. NEVER show `query_token` — it is an opaque sealed query whose
only use is save_user_dataset — but keep the chosen candidate's for the save.
- Samples show datetime columns as dates only; the built file keeps the full
timestamp, so never tell the user a timestamp is missing from a sample.
- Candidates can be near-identical: compare their descriptions, not just the samples.
Pass `target_format` when the user has named one (always for rss/atom). A candidate
with a column named `published_at` can't be saved (that name is reserved for the
build time): refine it with feedback asking to rename the column.
To refine, call again with the SAME `request`, previous_draft_id=<draft_id> and
`feedback`; ask for "ONE candidate" when the variants don't matter. Each round is a
new draft with its own draft_id. Ask before saving: name,
schedule, and (if not the suggestion) date mode.
Rate-limited to 20 drafts per hour per user. Requires auth (your MCP client attaches
the OAuth bearer automatically).