Widgets
Add widgets, wire them to data, and set their style and layout.
Contents
Tools
A widget is two halves: a pipeline (which data-source tools feed it, plus the transform that shapes the rows) and a payload (the chart or table config). add_widget creates it, and update_widget wires it (pipeline), dresses it (style) and places it (layout).
| Tool | What it does | |
|---|---|---|
list_widgets |
List widgets | Read-only |
get_widget |
Get widget | Read-only |
add_widget |
Add widget | Additive write |
update_widget |
Update widget | Destructive write |
get_chart_recipe |
Get chart recipe | Read-only |
render |
Render chart | Read-only |
remove_widget |
Remove widget | Destructive write |
list_widgets
Read-only
List all widgets on a dashboard: id, title, type (payload dialect), kind (the semantic renderer — kpi | table | pivot | progress for Vistazo-native widgets, the lead series type for ECharts widgets, markdown, visualization), layout, and the current pipelineId. ARCHIVED (removed) widgets are hidden by default — pass includeArchived:true to list them too, each marked archived:true; that is how a widget to restore is found without already knowing its id. Pass query instead to SEARCH widget titles and descriptions across every dashboard you can access in the workspace (dashboardId then optional). For one widget's full config and pipeline wiring, use get_widget; to retitle or edit a description, update_widget's style.
query
Search text: a full-text search across widget titles and descriptions in every dashboard you can access within the workspace, in place of one dashboard's list.
get_widget
Read-only · Reaches outside Vistazo
Read ONE widget; include picks what comes back (default config), and each value's rules are on include. config: the widget in full — title, description, type + semantic kind, archived (true = soft-deleted by remove_widget, restorable with its restore), layout, the stored payload config (echartsConfig / vistazoConfig / markdownContent / visualizationRef), the skill fields, and the complete pipeline wiring: every fetch stage's mcpId / toolName / argumentTemplate / role plus the persisted transform function body; it fetches nothing. data: the last published rows without refreshing, the cheap way to read a chart's numbers. inputs: config plus a live sample per fetch stage, to author a transform against. READ config BEFORE EDITING a widget: update_widget's pipeline and style replace what they touch, so fetch the current wiring/config here and re-send it with your modification instead of reconstructing it from guesses. One value returns that read alone; several return an object keyed by value.
include: inputs
the HYDRATE read for editing a widget's data — everything config returns PLUS, per fetch stage, the arguments as rendered against the dashboard's current parameter values, the alias the transform receives that stage under (inputs.<alias>; the first stage is also rawResponse), the full row count, and a fresh 3-row sample of the unwrapped payload, fetched live from the source now, nothing cached or written. Each stage also carries the source's note, and a partial result (hasMore) adds a warning naming the stage. Read it BEFORE writing or changing a transform, so it is authored against the real shape instead of a guess; then update_widget's pipeline with transformFunctionBody + fields and NO transformedData validates that transform against these same sources before anything is written. A stage whose source is unreachable comes back with error instead of a sample (a style edit never needs the data; a transform edit needs every stage reachable). Costs one call per stage against the live sources, so it is a deliberate authoring read, not a browse. Requires a connection to each stage's server (CONNECTION_NOT_AUTHENTICATED names set_connection as the remedy).
include: data
the widget's last refresh result without refreshing — the cheap way to read a chart's numbers; echoes title. Two freshness facts, never conflated: lastRefreshedAt is when the pipeline last RAN, latestPeriod ({field, value, granularity}) the newest period IN the rows. Refreshed minutes ago with latestPeriod 2019 means a pinned fetch window (check_freshness names the argument). latestPeriod is best-effort; null is never evidence of freshness. fields gives the units and precision each field RENDERS with — check it before reading meaning into a small difference; formatUnknown columns have unknown units. status is the last run (idle, running, or error with errorMessage); dataState the cache (populated, empty = a real zero-row result, never_materialized, missing, no_pipeline). With no rows, nextAction names the step; sourceOnly: true marks a widget that feeds a filter and draws no chart.
add_widget
Additive write
Add a widget to a dashboard. type names the PAYLOAD you will supply — vistazoConfig (a native kpi | table | pivot | progress, picked with kind), echartsConfig (an ECharts option), visualizationRef (a library entry, via entryId), markdownContent (prose, no pipeline) or file (one uploaded image or PDF via fileId, optional caption; no pipeline). Placing a file SHARES it with everyone who can view the dashboard, the public too when it is public. Where dialects overlap take the purpose-built kind: one value is a vistazoConfig kpi, never an ECharts gauge. The chart kind lives INSIDE the payload (series[].type, or kind). GRID: 12 columns; a row is 80px tall plus a 12px gap, so h rows draw h×80 + (h−1)×12 px (h=4 ≈ 356px). Widths divide the CONTAINER — the canvas beside the open sidebar (~315px), not the window. By container width: ≥996px the authored 12-column layout; 768–995px two per row in mobile order (widgets wider than 6 go full width); 480–767px widgets of width ≤3 pair up two per row and everything else is one per row; below 480px everything is one per row. Placement defaults to full width (w=12) below existing widgets; a narrower widget with no x/y goes beside the last row's widgets when they leave room for it, else below; pass x/y (as update_widget's layout) to place it exactly — a side-by-side row is chart {x:0,width:8} + KPI {x:8,width:4} at one y. A series with seriesField: "*" is a TEMPLATE that expands into one series per numeric row field no other series claims (one line per country when the countries are a multi_select). A file widget shows its file at once. The payload may ride on the create (markdownContent, echartsConfig or vistazoConfig, matching type, validated as update_widget's style validates it), so a heading or note is ONE call; without one the widget starts empty for update_widget's style. A data widget still needs update_widget's pipeline, then refresh, then VERIFY with get_widget include data (rowCount 0 means the fetch matched nothing). Requires edit access.
update_widget
Destructive write · Reaches outside Vistazo
Change an existing widget. Pass the sections you change, any mix in one call, applied in this order: pipeline REPLACES the whole data wiring (a fresh pipelineId), pipelineArguments edits only stage arguments in place, style sets the title, description and payload (it is how a widget is renamed), and layout moves or resizes it. Each section's own description holds its rules. READ get_widget FIRST: pipeline and a style payload replace what they touch, so re-send the current wiring or config with your change. A section that fails stops the call, and the reply names the sections already applied. Requires edit access.
style
The widget's presentation: its title, description, and the payload its type declares — echartsConfig, vistazoConfig (whose kind selects kpi | table | pivot | progress), markdownContent, or a file widget's caption. Markdown shows an uploaded image inline as , which shares that file with everyone who can view the dashboard (the public too when it is public). This section is also what RENAMES a widget — title and description live here — and what repoints a visualizationRef widget at another library entry (entryId). PARTIAL: fields you omit are left unchanged, so a title-only edit never touches the stored payload; a payload you do pass replaces the stored one and must match the widget's type. ECharts series binding: series[].name IS the row-field key the series reads — ADDING a series means adding an entry whose name is the new field's key; use seriesNames only when the DISPLAY label must differ from the field key (it renames while preserving the binding — never repurpose an existing series' name for display text). A series bound by seriesField may carry {{ params.<key> }} in its name, resolved like a title, so a highlighted series reads as the selected member. The echartsConfig field's own description is the one source for the drawable series types, the row shape each takes, the MUST flags whose absence draws a wrong chart, and the orientation / semantic-formatting / bar-zero rules — read it there rather than from here, and call get_chart_recipe for the composed forms (waterfall, Gantt, beeswarm, violin, maps, highlight-member to pick out one member, small-multiples for mixed magnitudes) and a complete kpi/table/pivot/progress config. For position/size/mobile-order use layout; for data wiring, pipeline. After an echartsConfig edit, render (kind widget) shows the card as drawn — clipped or overlapping labels, a legend over the plot.
layout
The widget's grid layout; only the fields you pass change. GRID: 12 columns; a row is 80px plus a 12px gap (h=4 ≈ 356px). Widths divide the CONTAINER (the canvas beside an open sidebar), not the window: ≥996px the authored layout; 768–995px two per row in mobile order (wider than 6 goes full width); 480–767px widths ≤3 pair up, the rest one per row; below 480px one per row. y is stacking order (empty rows compact away); x (0–11) and w (2–12) place within a row, x+w ≤ 12, so widgets sharing a y share a row ({x:0,w:4} beside {x:4,w:8}). h is 2–20, or 1 for a chromeless markdown widget. order (≥0) is the narrow-screen stack position; widgets with one sort before all without, so set it on every widget or none. Execute the user's desktop coordinates faithfully.
pipeline
Wires one or more connected MCP data-source tools to the widget — the REWIRE section. It REPLACES the whole pipeline (a fresh pipelineId; the old one is deleted), so read the wiring with get_widget first and re-send every stage with your change. To change only a stage's ARGUMENTS (a country code, a pinned end_year), use pipelineArguments: it edits them in place. Single tool: mcpId + toolName (+ argumentTemplate, run in the dashboard's workspace — no workspaceId in it); a <mcpId>__<tool> name is refused. Several: invocations (2+ stages fetched in parallel) feeding ONE transform, which receives inputs keyed by each stage's alias (else its tool name, _2/_3 on repeats) plus rawResponse for the first stage. Send transformFunctionBody + fields and OMIT transformedData: the server fetches every stage, runs your transform, seeds its rows, and REFUSES the write (error transform_degraded) if it throws, returns no rows from sources that had some, misses a declared field, or a source is unreachable. When every source returns nothing, zero rows publish with a note — never invent a placeholder row. get_widget include inputs samples each stage first. An EXTERNAL MCP client must send transformFunctionBody: a refresh it triggers never writes a transform. Transforms RESHAPE stage results and never carry data — no fetched values as literals. Refused with nothing written: an argument outside the tool's input schema, a {{ dates.<expr> }} outside the date grammar, a duplicate alias. Published rows must fit 900 KB (bytes, not rows). Rolling windows: see argumentTemplate; pair them with update_dashboard's cadence or the window stops at its last refresh. A pipeline serves ONE role: widget (chart data) or parameter_source (dropdown options). Its stages[] carry each source's note; it warns when a source returned a partial result (hasMore), every measure is 0 or null, rows leave cells empty, or the title/description still names an argument value you changed (fix with style / update_dashboard).
pipelineArguments
Stage edits that change the widget's fetch ARGUMENTS in place without re-sending its pipeline — the small-edit counterpart to pipeline, which replaces every stage, the transform and the fields and mints a new pipelineId. Here the pipelineId, the stage list, the persisted transform and the fields are untouched, and stages you don't name keep their arguments. Reach for it for "append ;ITA to country_code", "move end_year to 2025", "drop the pinned end bound so the window rolls". One entry per stage you change, each targeting by invocationId (from get_widget) or stageIndex (0-based); omit both when the pipeline has one stage. Per entry, EITHER setArguments / removeArguments (removing an argument that isn't there is refused) OR argumentTemplate to replace that stage's whole template. Values may be {{ params.<key> }} or {{ dates.<expr> }} holes; an untouched hole survives verbatim. Before anything is written the merged result is checked against the tool's input schema and the date grammar (a bad value is refused with nothing changed), and the stale-prose check runs; the response also warns when the republished rows leave cells empty, as pipeline does. With a persisted transform the merged arguments are also FETCHED and the transform re-run first: a patch that breaks it is refused (error transform_degraded), and one that works republishes the rows at once (rows.source 'validated'; zero rows when every source returned nothing); without one the previous rows stay ('previous') until refresh. The response echoes every stage's effective wiring (with the source's note when re-fetched; a partial result (hasMore) warns), before/after for what changed, so verify the merge rather than assume it.
get_chart_recipe
Read-only
Return the build recipe for a chart form — one of: waterfall, gantt, beeswarm, population-pyramid, violin, boxplot-outliers, xy-scatter, choropleth, flow-map, highlight-member, small-multiples, kpi, table, pivot, progress. highlight-member is the one for "pick out", "highlight" or "emphasise" ONE member (a country, a company, the selected filter value) against the rest — in a bar ranking, a line over its peers, or a reference line. small-multiples compares indicators of different magnitude, one panel each, where a shared axis lets the largest flatten the rest. kpi, table, pivot and progress each return a COMPLETE vistazoConfig to copy (a KPI's format, comparison/companion secondary and sparkline; a table's columns and rules) with the add_widget kind and size it needs. The composed forms are compositions of ordinary series and transform columns rather than ECharts series types, so the echartsConfig argument docs name them without spelling them out; each recipe returns a minimal config, the row shape the transform must emit, and the rules that make it draw correctly (a waterfall's stackStrategy, a Gantt's value-axis type, the atlas a choropleth resolves against). Call it before authoring one of those instead of guessing, and pass the config to update_widget's style. A form no series type or recipe draws (a ridgeline/joyplot, a marimekko) is a library visualization through save_visualization; never fake one from offset series. A static lookup: it reads no workspace data, fetches nothing, and writes nothing.
render
Read-only
Render a chart ON THE SERVER and read what it drew — call it BEFORE telling the user a chart renders or looks right, fix what it reports, render again. kind 'widget' draws an echartsConfig widget (id = widgetId) as its card does; kind 'visualization' runs a library entry (id = entryId) — after every save_visualization, and after its data changes. A chart that cannot draw returns ok:false with the error. Read the summary first. A widget draws its PUBLISHED rows and is refused without them (widget_rows_never_run, _error or _expired) — refresh first, so a blank chart always means the chart. A visualization takes rows, else widgetId's published rows, else its sampleRows. KPI, table and markdown widgets are refused. Headless: label widths are estimates, so a borderline hit is a hint, not a verdict. Both kinds share one hourly render ceiling.
remove_widget
Destructive write
Remove a widget — a SOFT DELETE: hidden everywhere, its pipeline PARKED, nothing destroyed — or bring a removed one back with restore: true, whose description holds the restore's rules. Confirm with the user only when the request does not identify the widget unambiguously ("drop the chart" on a board with three); when it names the widget, or the id you resolved matches what was asked, the request IS the confirmation. A removal's dashboardId must be the widget's own dashboard, or the call refuses. The response echoes its title and kind plus restorable:true. No cleanup call follows: it leaves list_widgets (unless includeArchived:true, and its query), get_dashboard, the canvas and the public link, and its pipeline stops running and spending. get_widget still reads it by id; every other write refuses while it is archived. Requires edit access.
restore
true brings back a widget remove_widget archived, with its pipeline, wiring, transform and last published rows intact — the widget counterpart of archive_dashboard's restore:true. Not manage_version, which rolls a whole dashboard back to a version. Find it with list_widgets includeArchived:true. It returns to its STORED grid slot; if a widget added meanwhile sits there, compaction pushes one down, so the response echoes its position for update_widget's layout. Its data is as old as the removal: refresh to bring it current. Refuses a widget that is not archived.