---
title: MCP tools
description: Every Dashies MCP tool, grouped by job, with the shared rules and the protocol facts every call depends on.
updated: 2026-09-22
---

Dashies publishes through an MCP server at `https://mcp.dashies.ai/mcp`. Your AI
tool calls these tools; you do not call them by hand. This section is the
contract: what each tool takes, what it returns, which errors it can raise, and
which workspace it acts on.

**No number is given for how many tools there are, and the pages below are the
answer.** This page said "There are nineteen tools" while twenty-one pages sat
under it, so the one number a reader could not check against the page in front of
them was the one that was wrong.

A build guard does read the live tool list out of the server's source, and it
fails this site when a tool has no page here, when a page documents a tool that no
longer exists, when a page's stated required parameters disagree with the real
schema, and when a page documents a parameter the schema does not have. **What no
arm of that MCP block reads is a count written in prose.** That distinction matters
because the sentence deleted above claimed the opposite, in the words "the count is
not decorative" - so a reader had been told the number was machine-checked when
nothing checked it. Every MCP arm is per tool; none is per total. (The same script
DOES bind to a prose heading elsewhere, `## The eighteen types` for tile types, and
says renaming it must fail rather than silently check nothing. That is a different
subject, deliberately bound, and it is the exception that makes the sentence above
worth stating precisely rather than as a rule about the whole guard.)

**AND THE TABLES BELOW ARE GUARDED LESS THAN THE PAGES ARE, WHICH IS WHY THIS PAGE
WAS ABLE TO GO WRONG IN A SECOND WAY AT THE SAME TIME.** This page carries no
`tools:` frontmatter, so the drift guard treats it as unchecked end to end: it
verifies that every tool HAS a page, not that this index LINKS one. Both were true
at once here - the prose said nineteen, twenty-one pages existed, and these tables
listed twenty, `verify_dashboard` having no row. It was added in this change. **If
you are adding a tool, adding its page is what CI checks and adding its row here is
not**, so do the second by hand.

## Before the per-tool pages

Two pages hold everything the tool pages would otherwise repeat. Read them once.

- [Server and protocol](/reference/mcp-tools/protocol) covers the endpoint, the
  JSON-RPC transport, version negotiation, what `initialize` advertises, the full
  method list, and the shape every tool result arrives in.
- [Shared rules](/reference/mcp-tools/conventions) covers paths and slugs,
  metadata omit-versus-clear semantics, connection resolution, rate limits, and
  which workspace a call acts on.

## Publishing and lifecycle

| Tool | What it does |
|---|---|
| [`publish_dashboard`](/reference/mcp-tools/publish-dashboard) | Compile and publish a Dashies spec. |
| [`update_dashboard`](/reference/mcp-tools/update-dashboard) | Edit metadata or rename, without republishing. |
| [`get_dashboard`](/reference/mcp-tools/get-dashboard) | Read back one published file by path. |
| [`delete_dashboard`](/reference/mcp-tools/delete-dashboard) | Retire a dashboard by its canonical slug. |
| [`list_dashboards`](/reference/mcp-tools/list-dashboards) | Enumerate your dashboards, newest-updated first. |

## Versions

Every republish of an existing slug archives the prior body first. These four
tools are how you see, name, inspect and roll back to those snapshots.

| Tool | What it does |
|---|---|
| [`list_dashboard_versions`](/reference/mcp-tools/list-dashboard-versions) | List a dashboard's snapshots, newest first. |
| [`restore_dashboard_version`](/reference/mcp-tools/restore-dashboard-version) | Roll the body back to a chosen snapshot. |
| [`get_dashboard_version`](/reference/mcp-tools/get-dashboard-version) | Read one snapshot's body without restoring it. |
| [`update_dashboard_version`](/reference/mcp-tools/update-dashboard-version) | Name a snapshot, or clear its name. |

## Authoring against data

The loop that produces a refreshable dashboard: check what you already have,
find a connection, read its columns, explore the data behind them, run the
candidate SQL once, then publish.

| Tool | What it does |
|---|---|
| [`check_readiness`](/reference/mcp-tools/check-readiness) | What Dashies knows about your account, and the single next step. Call it first. |
| [`list_connections`](/reference/mcp-tools/list-connections) | List the data connections you can author against: warehouses, and the uploaded-file source. |
| [`introspect_schema`](/reference/mcp-tools/introspect-schema) | List the tables and columns a cube SQL may read. |
| [`validate_cube_sql`](/reference/mcp-tools/validate-cube-sql) | Run candidate cube SQL once, read-only, and report on it. |
| [`explore_data`](/reference/mcp-tools/explore-data) | Run one read-only SELECT and get a small, complete answer. |

:::warning{title="Validation is not verification"}
`validate_cube_sql` proves the SQL runs and returns a shape. It does not prove
the numbers are right. Cross-check each additive measure against an independent
aggregate over the un-joined base table before you trust a published figure.
:::

## Uploaded files

A dashboard can be built from a CSV or Excel file the user holds instead of a
warehouse. The file is sent by a `curl` command in the agent's own shell, never
through the conversation. The upload becomes the workspace's uploaded-file source
and can be introspected, explored, validated and published like the other authoring sources.

| Tool | What it does |
|---|---|
| [`create_file_upload`](/reference/mcp-tools/create-file-upload) | Get a one-time upload URL and the exact `curl` command that sends the file. |
| [`get_file_upload`](/reference/mcp-tools/get-file-upload) | Read an upload's status, its SHA-256 and its catalog, or wait for its conversion. |
| [`replace_file_upload`](/reference/mcp-tools/replace-file-upload) | Point every dashboard on one uploaded file at a newer upload of that file, keeping each dashboard's slug, URL and history. |

## Refresh: observability and control

| Tool | What it does |
|---|---|
| [`get_refresh_status`](/reference/mcp-tools/get-refresh-status) | Read the refresh state and the last 20 runs. |
| [`get_source_config`](/reference/mcp-tools/get-source-config) | Read back the stored refresh manifest verbatim. |
| [`set_refresh_schedule`](/reference/mcp-tools/set-refresh-schedule) | Set the cadence, interval and day/time anchor. |
| [`trigger_refresh`](/reference/mcp-tools/trigger-refresh) | Refresh now instead of waiting for the schedule. |
| [`verify_dashboard`](/reference/mcp-tools/verify-dashboard) | Ask the query tier the questions your page will ask, before a reader does. |

## Spec

| Tool | What it does |
|---|---|
| [`get_dashboard_spec`](/reference/mcp-tools/get-dashboard-spec) | Read back a spec-backed dashboard's spec and its hash. |
| [`derive_dashboard_spec`](/reference/mcp-tools/derive-dashboard-spec) | Reconstruct a draft spec from a pre-spec dashboard. |

## What is gated

These are the things on this surface that depend on your plan. No count is given,
because this sentence said "two" and the next gated tool would have made it false.

- `trigger_refresh` requires a paid plan. The refusal is
  `Refreshing on demand requires a paid plan.`
- `create_file_upload` requires a paid plan or a trial. The refusal is
  `A paid plan is required to upload a file.`
- Any use of a **warehouse** connection requires a paid plan. That covers
  `introspect_schema`, `validate_cube_sql`, and a `source_config.connection` that
  is a connection id. The check happens in the database, not in the tool, so the
  refusal surfaces as the connection not resolving rather than as a plan message.

There is **no publish quota**. Per-plan publish limits were removed; the only
limit on publishing is the rate limit of 60 calls per user per minute. Access is
not plan-gated either: every published dashboard belongs to a workspace and is
members-only on every plan.

## Check it worked

Ask your AI tool to list your dashboards. If the connection is live you get a
bulleted list plus a `BEGIN_JSON` block; if it is not, the client re-runs the
browser sign-in and then answers. Either outcome tells you where you are.
