# Vistazo as an MCP server

## What can your agent do in Vistazo?

Your agent can build and run Vistazo dashboards through a set of tools served over MCP, an open
standard for connecting AI agents to software. Add the server to Claude, Claude Code, Cursor, Codex,
Gemini CLI, VS Code or any client that supports remote MCP servers. [Connect your own agent](/docs/mcp/connect)
has the setup for each one.

```
https://mcp.vistazo.ai/mcp
```

Claude Code, Codex and Cursor, which load tools only when they need them, take
`https://mcp.vistazo.ai/mcp/full`. Gemini CLI and VS Code, which load every tool up front, take
`https://mcp.vistazo.ai/mcp/slim`.
[Connect your own agent](/docs/mcp/connect) lists what each address carries.

Your agent does the thinking, on its own model and its own plan. The tools carry out what it asks.
They create a dashboard, wire a widget to a source, set a schedule, or return what a dashboard
currently shows.

The server also carries the tools of the sources connected in your workspaces, and its `read_docs`
tool returns any page of these docs. A plain-text index of every page is at
[/llms.txt](https://vistazo.ai/llms.txt).

## Which tools does the server have?

About 55 tools, documented in groups.

| Group | What it covers |
|---|---|
| [Identity and workspaces](/docs/mcp-tools/identity) | Who your agent acts as, and the workspaces it can reach |
| [Dashboards](/docs/mcp-tools/dashboards) | Create, find, describe, copy, theme and archive dashboards |
| [Versions](/docs/mcp-tools/versions) | Save and roll back dashboards, skills, notebooks and visualizations |
| [Widgets](/docs/mcp-tools/widgets) | Add widgets, wire them to data, and set their style and layout |
| [Parameters](/docs/mcp-tools/parameters) | The filters and date ranges a dashboard's widgets read |
| [Refresh and freshness](/docs/mcp-tools/refresh) | Fetch new data, check how current it is, and try values without publishing |
| [Metric alerts](/docs/mcp-tools/alerts) | Your own thresholds, changes and anomaly alerts on a widget |
| [Sharing and access](/docs/mcp-tools/sharing) | Public links and embeds |
| [Data connections](/docs/mcp-tools/connections) | The data sources a workspace reads from |
| [Datasets](/docs/mcp-tools/datasets) | Tables from the files you upload |
| [Skills](/docs/mcp-tools/skills) | Procedures written in your workspace, loaded before the work they cover |
| [Workspace knowledge](/docs/mcp-tools/knowledge) | The metrics and dimensions your dashboards declare, and the notes members sign on them |
| [Notebooks](/docs/mcp-tools/notebooks) | Markdown with Python cells |
| [Visualization library](/docs/mcp-tools/visualizations) | Chart code for charts the built-in widgets cannot draw |
| [Product docs](/docs/mcp-tools/docs) | Any page of these docs |
| [Workspace administration](/docs/mcp-tools/administration) | Members, invitations, usage and the audit log |
| [Governance](/docs/mcp-tools/governance) | Assets that need an owner decision |

Your client loads only the tools you can use. The administration and governance tools appear only if
you are an admin of at least one workspace, and a call against a workspace you do not administer is
refused.

Every tool carries the hints MCP defines for when a client should ask you first. Read-only tools are
marked read-only. Each write is marked destructive or not, and your client uses those marks to
decide which calls to confirm with you.

## How does your agent read a dashboard?

`get_dashboard` returns a dashboard's widgets, its parameters and a written summary of what it shows.
The summary includes a plain-language reading of each widget, which often answers a question without
fetching anything. `get_widget` with `include: ["data"]` returns a widget's last published rows,
also without a fetch. `refresh` fetches new data now and publishes it to everyone who views the
dashboard.

`list_dashboards` with a `query` searches names, tags and descriptions and ranks the matches. Without
one it lists every dashboard you can open, with its widget count, owner and last update.

## How does your agent build a widget?

A widget is a pipeline and a payload. The pipeline is one or more stages, each a call to a source
tool, followed by one transform, a short script that reshapes their results into rows for the chart. The payload is the chart's configuration.

`update_widget` changes a widget in sections. Its `pipeline` replaces the whole pipeline, and
`pipelineArguments` changes only a stage's arguments. Unless your agent also sends the finished rows,
`pipeline` runs the transform first and refuses one that fails. A broken transform never reaches the
dashboard that way.

Your agent writes the transform. A refresh your agent starts re-runs the saved transform and never
writes a new one. When a source changes shape and the transform no longer fits, that refresh reports
`stale_transform`, and your agent sends a corrected transform. A scheduled refresh repairs the
transform itself, unless an admin has turned off **Repair broken charts with AI** under
**Settings → Assistant**.

## What does a first build look like?

This walkthrough builds a dashboard over the hosted Countries source. It has a population chart, a
headline figure, a country filter and a weekly refresh. Each step names the tool and the arguments
that matter.

1. `get_me` lists your workspaces. Take the `workspaceId` of one marked reachable.
2. `create_dashboard` with `workspaceId` and `name` returns the new dashboard's id.
3. `set_parameter` adds the filter. Pass `key: "country"`, `label: "Country"`, `type: "select"`,
   `options` as value and label pairs such as `{"value": "BRA", "label": "Brazil"}`, and a default
   `value` of `"BRA"`. Define a parameter before any pipeline that reads it, because a pipeline
   renders an undefined parameter as empty. Options you list by hand are not checked against the
   source, so confirm the codes first.
4. `add_widget` twice. The chart takes `type: "echartsConfig"`. The headline figure takes
   `type: "vistazoConfig"` with `kind: "kpi"`. Each call returns a widget id.
5. `update_widget` for each widget, with the same `pipeline`. A hosted source's `mcpId` is its
   lowercase name, here `countries`, and `list_mcp_connections` lists every id in the workspace.
   `toolName` is the tool's own name, `get_data`. `argumentTemplate` holds the tool's arguments as a
   JSON string, where `{{ params.country }}` stands for the filter's value.

   ```json
   {"database_id": "WB_WDI", "indicator_id": "WB_WDI_SP_POP_TOTL", "country_code": "{{ params.country }}", "start_year": 2000}
   ```

   `transformFunctionBody` is the body of a JavaScript function. With one stage it receives the
   source's result as `rawResponse`. With several it receives `inputs`, keyed by each stage's alias.
   It returns an array of flat rows whose values are strings, numbers, booleans or null.

   ```js
   return rawResponse.rows
     .map(r => ({ year: Number(r.time_period), population: r.obs_value }))
     .sort((a, b) => a.year - b.year)
   ```

   `fields` lists each row key with a label and a type, `dimension` or `measure`. The server fetches
   the source, runs the transform and refuses the write if it throws or returns no rows. The rows it
   produced are published, so the widget shows data at once.
6. `update_widget` with a `style` sets each payload, the configuration that draws the widget. The
   chart's goes in `echartsConfig`. In an ECharts chart each series' `name` is the row field it
   reads, so this draws `population` by year.

   ```json
   {"xAxis": {"type": "category"}, "yAxis": {"type": "value"}, "series": [{"type": "line", "name": "population"}]}
   ```

   The headline figure takes `{"kind": "kpi", "valueColumn": "population"}` as its `vistazoConfig`,
   which shows the last row. `get_chart_recipe` returns complete configurations for other forms. A
   payload can also ride on `add_widget`, and `style` can ride in the same call as step 5's
   `pipeline`.
7. `update_dashboard` with `dashboardId` and `cadence: { enabled: true, interval, unit }`. Match the
   interval to how often the source publishes. Countries data is annual, so `interval: 1` with
   `unit: "weeks"` keeps it current, where a daily schedule fetches the same figures 365 times a
   year. The shortest interval is 15 minutes, and each person can schedule 25 dashboards in a
   workspace. [Limits](/docs/reference/limits) lists both.
8. `refresh` with `dashboardId`. The rows are already published after step 5. The first refresh also
   records the shape of each source's response, which a scheduled run uses to notice when a source
   changes.
9. `get_widget` with `include: ["data"]` for each widget. A reply with `ok: true` and `rowCount: 0`
   means the fetch matched nothing.
10. `render` with `kind: "widget"` and the chart's id draws the chart on the server and reports what
   went wrong, such as clipped labels. KPI, table and markdown widgets are not rendered this way.

## How does your agent reach your sources?

The same server reaches the tools of every source connected in the workspaces you grant your agent,
plus the five public sources Vistazo hosts. Your agent finds one with `search_tools` and runs it
with `call_tool`, or `call_write_tool` for one that changes something. It adds no other server and
holds no credentials. [Your connected sources](/docs/mcp/gateway) covers which workspace a call runs
in and which tools may run.

## What does a tool call cost?

Calling a tool is free. So are the refreshes and data fetches your agent sets off, the hosted sources
and your own datasets included. `upload_file` is free because Vistazo reads a file's tables without a
model, and the file counts toward your storage. Your agent's own model calls never count toward
anything in Vistazo.

A few things count toward an allowance exactly as they would from the app:

- `run_notebook` counts as compute, for each second the cells run.
- A scheduled refresh that repairs a transform counts as inference.
- A dashboard's written summary is rewritten in the background after edits, and that counts as
  inference.

[Usage](/docs/billing/usage) explains the allowances. Rate limits apply to every request your agent
sends, and they are listed in [Limits](/docs/reference/limits).
