Turn CSV into an animated chart video with Furlen — every figure recomputed from your rows first.
Three tools with complete schemas, strong descriptions (194 - 392 chars), and proper parameter typing via Zod. Tool names are action-oriented (furlen_public_data, furlen_render, furlen_render_status). Descriptions are exceptionally detailed, including error codes, rate limits, and recovery guidance. Tool annotations (readOnlyHint, idempotentHint, openWorldHint) are present on furlen_public_data. However, furlen_render and furlen_render_status lack annotations despite having clear read/write semantics. Output schemas are not formally documented in the code, responses are returned as JSON text via the `text()` helper, making downstream field expectations implicit rather than explicit. Parameter descriptions are thorough but could benefit from structured constraints (e.g., explicit enum validation in schema, not just description text).
Fetch a real public dataset by describing it in plain English — no CSV needed. Pulls from the World Bank, IMF WEO, FRED, Eurostat, UN Comtrade, WHO, and company filings (SEC EDGAR, ESEF, DART, EDINET). Returns the rows, a ready-to-use `csv` string you can pass straight to furlen_render's `csv` argument, and provenance naming the exact indicator and source. Use this when the subject is a public statistic. If the user already has their own numbers, skip it and call furlen_render directly. Read-only: it fetches, it does not create anything. Needs an API key with the `data:read` scope — keys minted before July 2026 do not have it and get 403 `insufficient_scope`, which is fixed by creating a new key, not by retrying. Limited to 6 requests/minute. If no connected source covers the subject it says so rather than guessing: 422 `subject_unsupported` means the data does not exist here, not that the request was malformed, so do not retry it reworded. Related refusals — `metric_unavailable`, `entity_not_found`, `currency_mismatch`, `compare_no_overlap` — are equally final; report them to the user instead of rephrasing.
Render a CSV into an animated, claim-verified data-story video. Returns immediately with a `renderId` — it does NOT return the video. Poll furlen_render_status until it completes; renders usually take 30–90 seconds. Use this when someone wants data turned into a video or animation. For a public statistic, call furlen_public_data first and pass its `csv` field straight through. Use furlen_render_status, not this tool, to check on a render already started — calling this again starts a second, unrelated render. The export is fail-closed, and this is the part worth knowing before you call it: Furlen writes the narration, then recomputes every numeric claim in it against your rows. If a claim does not reconcile you get 422 `claim_unverified` listing the offending claims, and nothing is rendered. That signals ambiguous or internally inconsistent data — show the user the issues rather than retrying, because an identical request will be refused identically. Side effects: stores a story and queues a render job on the caller's workspace. Requires the `renders:write` scope; limited to 10 requests/minute, with `X-RateLimit-*` on every response. There is no idempotency key, so a retried call renders twice. Structural limits are 100,000 rows, 500 columns and 32,768 characters per cell — a breach returns 422 with the specific code (e.g. `LIMIT_TOO_MANY_ROWS`, `HEADER_DUPLICATE`). Data with nothing worth saying returns 422 `no_insights`.
Output schemas not formally documented. Responses returned as JSON text via text() helper; downstream field expectations (renderId, status, downloadUrl, error) are implicit in descriptions, not explicit in schema definitions. LLMs cannot reliably extract typed fields for chaining.
furlen_render and furlen_render_status lack tool annotations. furlen_render is destructive (writes, queues render job, no idempotency key) but has no destructiveHint. furlen_render_status is read-only but lacks readOnlyHint. Annotations guide agent retry logic and safety planning.
Parameter constraints stated in descriptions but not enforced in schema. furlen_render 'audience' and 'outputFormat' are enums in description text, but Zod schema does not validate them. furlen_render 'resolution' enum is similarly description-only. LLMs may pass invalid values; validation happens server-side, not client-side.
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-23 | C | 63 | 2026-07-28+ | v2 |
Check a render started by furlen_render. Returns `status` (queued → running → completed | failed | cancelled), `progress` 0–100, and — once completed — a `downloadUrl`. Poll every few seconds; renders usually take 30–90 seconds, and a job that stalls is automatically requeued, so `running` for a while is not a failure. Read-only, and the right tool for any 'is it done yet' question: never re-call furlen_render to check on an existing render. Requires the `renders:read` scope and allows 60 requests/minute, which is what makes a few-second poll interval safe. `downloadUrl` appears only on `completed` and is absolute. Fetching it needs the `exports:read` scope and mints a link valid for one hour, so download promptly rather than storing the URL. On `failed`, the `error` field says why.
Error recovery guidance is excellent in descriptions (e.g., 'subject_unsupported means data does not exist, do not retry reworded'), but error responses from the api() function are generic: 'Furlen API {status}: {error}'. Structured error codes and recovery hints should be returned in tool responses, not buried in descriptions.
No pagination or result-limiting guidance for furlen_public_data. Description does not specify max rows returned or whether results are paginated. If a query returns thousands of rows, the CSV string could exhaust context. Baseline pattern expects pagination or explicit limits.