> ## Documentation Index
> Fetch the complete documentation index at: https://docs.erdo.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# MCP Server

> Connect AI assistants and applications to your data via Erdo's MCP server

# MCP Server

Erdo exposes a [Model Context Protocol (MCP)](https://modelcontextprotocol.io) server that lets AI assistants and applications query your datasets, ask data questions, manage conversations, and automate analysis — all using your existing Erdo permissions.

Use it to:

* **Connect AI assistants** like Claude Desktop, Cursor, or Windsurf to your data
* **Build AI-powered apps** that query and visualize your data using any MCP client library
* **Integrate with any LLM** via Vercel AI SDK, LangChain, or direct MCP client connections
* **Automate recurring analysis** with heartbeat automations
* **Manage knowledge** with memories and skills that persist across conversations

## Quick Start

### 1. Get an API Key

Click your profile in the bottom-left corner of Erdo and go to **API Keys**. Create a new key and copy the token.

### 2. Connect to the MCP Server

The MCP endpoint is `https://api.erdo.ai/mcp` using [Streamable HTTP transport](https://modelcontextprotocol.io/specification/2025-03-26/basic/transports#streamable-http). Any MCP-compatible client can connect. The organization is inferred from your API key automatically.

<Tabs>
  <Tab title="Claude Desktop">
    Add to your `claude_desktop_config.json`:

    ```json theme={null}
    {
      "mcpServers": {
        "erdo": {
          "url": "https://api.erdo.ai/mcp",
          "headers": {
            "Authorization": "Bearer YOUR_API_KEY"
          }
        }
      }
    }
    ```
  </Tab>

  <Tab title="Claude Code">
    ```bash theme={null}
    claude mcp add erdo \
      --transport http \
      --url https://api.erdo.ai/mcp \
      --header "Authorization: Bearer YOUR_API_KEY"
    ```
  </Tab>

  <Tab title="Cursor">
    Add to your `.cursor/mcp.json`:

    ```json theme={null}
    {
      "mcpServers": {
        "erdo": {
          "url": "https://api.erdo.ai/mcp",
          "headers": {
            "Authorization": "Bearer YOUR_API_KEY"
          }
        }
      }
    }
    ```
  </Tab>

  <Tab title="Custom App">
    Connect from any MCP client library (TypeScript, Python, Go, etc.):

    ```typescript theme={null}
    import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
    import { Client } from '@modelcontextprotocol/sdk/client/index.js';

    const client = new Client({ name: 'my-app', version: '1.0.0' });
    await client.connect(new StreamableHTTPClientTransport(
      new URL('https://api.erdo.ai/mcp'),
      {
        requestInit: {
          headers: {
            'Authorization': 'Bearer YOUR_API_KEY',
          },
        },
      },
    ));

    // List available tools
    const { tools } = await client.listTools();

    // Call a tool
    const result = await client.callTool({
      name: 'erdo_list_datasets',
      arguments: {},
    });
    ```
  </Tab>
</Tabs>

To target a different organization that the API-key user belongs to, add
`X-Organization-ID: <org-slug>`. To keep work inside one project, also add
`X-Project-ID: <project-uuid>`. The project must belong to the selected
organization. Both headers are optional; omit `X-Project-ID` for the default
**All projects** context.

### 3. Start Using It

Once connected, the MCP client can discover and call Erdo tools. In AI assistants, try asking:

* "List my datasets in Erdo"
* "What columns does the sales dataset have?"
* "How many orders were placed last month?" (uses the Data Question Answerer agent)
* "Run a SQL query on my customers dataset to find the top 10 by revenue"
* "Create a heartbeat that checks for anomalies in my revenue data every hour"

## Available Tools

Erdo exposes MCP tools across data, threads, knowledge, KV stores (the shared key/value store, a.k.a. collections), artifacts, pages, agent runs, and automations. Three Platform-API surfaces have their own dedicated pages, each with the full MCP tool, REST, and CLI reference: [Evals](/evals), [Workstreams](/workstreams), and [Experiments](/experiments).

### Data Tools

#### erdo\_list\_datasets

List all datasets in your organization with name, type, description, and status.

**Parameters:**

| Parameter | Type   | Description                         |
| --------- | ------ | ----------------------------------- |
| `limit`   | number | Optional. Max results (default 20). |

#### erdo\_search\_datasets

Search datasets by name or description.

**Parameters:**

| Parameter | Type   | Description                         |
| --------- | ------ | ----------------------------------- |
| `query`   | string | Search text                         |
| `limit`   | number | Optional. Max results (default 20). |

#### erdo\_get\_dataset\_schema

Get detailed schema for a dataset including column names, types, statistics, and sample data.

**Parameters:**

| Parameter    | Type   | Description  |
| ------------ | ------ | ------------ |
| `dataset_id` | string | Dataset UUID |

#### erdo\_gather\_dataset\_context

Get detailed context for multiple datasets at once — schemas, column types, statistics, descriptions, and sample data. Useful for understanding your data landscape before asking questions.

**Parameters:**

| Parameter       | Type      | Description                                                 |
| --------------- | --------- | ----------------------------------------------------------- |
| `dataset_slugs` | string\[] | Optional. Specific dataset IDs or slugs. Empty returns all. |
| `limit`         | number    | Optional. Max datasets to return (default 10).              |

#### erdo\_fetch\_dataset\_contents

Fetch raw contents of a dataset. Returns rows and columns directly without requiring a SQL query. Useful for exploring small datasets or getting a quick preview.

**Parameters:**

| Parameter      | Type   | Description                   |
| -------------- | ------ | ----------------------------- |
| `dataset_slug` | string | Dataset UUID or slug          |
| `limit`        | number | Optional. Max rows to return. |

#### erdo\_run\_query

Run a raw SQL query directly against a dataset and return rows and columns. Use this when you already know the exact SQL you want to run. The SQL dialect depends on the dataset's storage backend (PostgreSQL, ClickHouse, or DuckDB for file-based datasets).

**Parameters:**

| Parameter      | Type   | Description                                 |
| -------------- | ------ | ------------------------------------------- |
| `dataset_slug` | string | Dataset UUID or slug to query               |
| `query`        | string | SQL query to execute                        |
| `limit`        | number | Optional. Max rows to return (default 100). |

#### erdo\_query\_data

Query a dataset using natural language. Describe what data you want and Erdo will generate and execute the SQL query for you.

**Parameters:**

| Parameter      | Type   | Description                                                        |
| -------------- | ------ | ------------------------------------------------------------------ |
| `question`     | string | Natural language question, e.g. "show top 10 customers by revenue" |
| `dataset_slug` | string | Dataset UUID or slug to query                                      |

**Returns:** the generated `sql`, the result values as `columns` + `rows` (the
same tabular shape `erdo_fetch_dataset_contents` returns), `row_count` — how many
rows the query matched, which can exceed the rows returned since those are capped
at 1000 — and `output`, the result rendered to read. `applied_filters` names any
of the dataset's saved filters the read ran under, so a count is never reported
as the whole truth when it excludes something.

#### erdo\_ask\_data\_question

Ask a natural language question about your data. This invokes Erdo's Data Question Answerer agent, which analyzes datasets, writes and executes code, and returns a text answer. To visualize results, use `erdo_render_chart` or `erdo_render_table`.

<Warning>
  This tool can take 30 seconds to 2 minutes for complex questions, as it runs a full AI analysis pipeline.
</Warning>

**Parameters:**

| Parameter       | Type      | Description                                        |
| --------------- | --------- | -------------------------------------------------- |
| `question`      | string    | The data question to answer                        |
| `dataset_slugs` | string\[] | Optional. Dataset slugs to scope the question to.  |
| `timezone`      | string    | Optional. User timezone (e.g. `America/New_York`). |

**Returns:** A thread ID (for follow-up in the Erdo UI) and the agent's text answer.

#### erdo\_render\_chart

Render a data visualization chart. Supports bar, line, pie, histogram, and scatter chart types. The chart fetches data directly from the dataset — no embedded data needed.

**Parameters:**

| Parameter        | Type      | Description                                                               |
| ---------------- | --------- | ------------------------------------------------------------------------- |
| `chart_type`     | string    | Chart type: `bar`, `line`, `pie`, `histogram`, or `scatter`               |
| `chart_title`    | string    | Title for the chart                                                       |
| `x_axis`         | object    | X-axis configuration (label, key, format, value\_type)                    |
| `y_axes`         | object\[] | Y-axis configurations                                                     |
| `series`         | object\[] | Data series, each with `dataset_slug`, `key`, `sql_query`, `resource_key` |
| `data_reduction` | object    | Data reduction strategy (none, sample, aggregate, bin)                    |
| `stacked`        | boolean   | Whether to stack bars (for bar charts)                                    |
| `sort`           | object\[] | Sort conditions                                                           |

#### erdo\_render\_table

Render a data table. The table fetches data directly from the dataset.

**Parameters:**

| Parameter      | Type           | Description                                                 |
| -------------- | -------------- | ----------------------------------------------------------- |
| `table_title`  | string         | Title for the table                                         |
| `dataset_slug` | string         | Dataset slug                                                |
| `columns`      | object\[]      | Column definitions (column\_name, key, format, value\_type) |
| `sql_query`    | string \| null | Optional SQL query to filter/transform data                 |
| `resource_key` | string \| null | Required for file datasets (CSV/Excel)                      |

#### erdo\_create\_dataset

Create a new empty dataset. After creation, use `erdo_write_rows` to add data. The dataset uses your organization's default storage backend.

**Parameters:**

| Parameter      | Type   | Description                                                  |
| -------------- | ------ | ------------------------------------------------------------ |
| `name`         | string | Name for the dataset                                         |
| `description`  | string | Optional. Description.                                       |
| `instructions` | string | Optional. Instructions for AI agents analyzing this dataset. |

**Returns:** The created dataset with `id`, `slug`, `name`, `type`, and `status`.

#### erdo\_upload\_dataset\_file

Upload a file (CSV, TSV, Excel, JSON, JSONL, PDF, DOCX, TXT, Markdown, ...) and create a dataset from it in one call. The schema is extracted before the tool returns, so the dataset is immediately queryable. Prefer this over `erdo_create_dataset` + `erdo_write_rows` when the data already exists as a file.

**Parameters:**

| Parameter        | Type   | Description                                                                                    |
| ---------------- | ------ | ---------------------------------------------------------------------------------------------- |
| `filename`       | string | Filename with extension — drives type detection                                                |
| `content_base64` | string | The raw file bytes as standard base64 (max 20 MB decoded; larger files go through the web app) |
| `name`           | string | Optional. Display name; defaults to the filename.                                              |
| `description`    | string | Optional. Description shown to agents analyzing the dataset.                                   |

**Returns:** `{ dataset_id, slug, name, ready }` — `ready` is `false` when the file stored but its schema could not be extracted (not yet queryable).

#### erdo\_delete\_dataset

Delete a dataset and all its data. This is permanent.

**Parameters:**

| Parameter      | Type   | Description                    |
| -------------- | ------ | ------------------------------ |
| `dataset_slug` | string | Dataset UUID or slug to delete |

#### erdo\_write\_rows

Write or upsert rows to a dataset. For database-backed datasets (Postgres, ClickHouse), set `key_column` to upsert — matching rows are updated, new rows are inserted. For file datasets (CSV), rows are always appended.

**Parameters:**

| Parameter      | Type      | Description                                       |
| -------------- | --------- | ------------------------------------------------- |
| `dataset_slug` | string    | Dataset slug to write to                          |
| `rows`         | object\[] | Array of row objects (column name → value)        |
| `key_column`   | string    | Optional. Column for upsert (update on conflict). |

**Returns:** `{ rows_affected: number }`

#### erdo\_delete\_rows

Delete rows from a dataset. Works for both file (CSV) and database-backed datasets.

**Parameters:**

| Parameter      | Type      | Description                                                 |
| -------------- | --------- | ----------------------------------------------------------- |
| `dataset_slug` | string    | Dataset slug to delete from                                 |
| `key_column`   | string    | Optional. Column to match keys against.                     |
| `keys`         | string\[] | Optional. Key values to delete. If empty, deletes all rows. |

**Returns:** `{ rows_affected: number }`

#### erdo\_update\_dataset\_schema

Update a dataset's schema: add, remove, rename columns, or change column types. Operations are applied atomically — if any fails, none are applied. Supported for CSV file datasets only. After changes, analysis is automatically refreshed.

**Parameters:**

| Parameter      | Type      | Description                           |
| -------------- | --------- | ------------------------------------- |
| `dataset_slug` | string    | Dataset UUID or slug                  |
| `operations`   | object\[] | Schema operations to apply atomically |

**Operation object:**

| Field         | Type   | Description                                                                                         |
| ------------- | ------ | --------------------------------------------------------------------------------------------------- |
| `type`        | string | `add_column`, `remove_column`, `rename_column`, or `alter_column_type`                              |
| `column`      | string | Target column name                                                                                  |
| `new_name`    | string | New name (for `rename_column` only)                                                                 |
| `column_type` | string | Type hint: `text`, `integer`, `float`, `date`, `boolean` (for `add_column` and `alter_column_type`) |

**Returns:** `{ columns_added, columns_removed, columns_renamed, columns_retyped, current_columns }`

### Integration Tools

Connect third-party apps and data sources without leaving your AI assistant. Credential-based apps (databases, API keys, service accounts) connect in one call; OAuth apps return a `connect_url` for the user to open in a browser, and `erdo_check_integration_connection` completes the loop.

#### erdo\_list\_integrations

List the integrations connected in your organization with app identifier, name, status, and auth type.

#### erdo\_search\_integration\_apps

Search apps that can be connected — native integrations (databases, warehouses, APIs) plus thousands of SaaS apps.

**Parameters:**

| Parameter | Type   | Description                                                                                          |
| --------- | ------ | ---------------------------------------------------------------------------------------------------- |
| `query`   | string | Optional search term, e.g. `slack` or `postgres`. Empty lists native integrations plus popular apps. |

**Returns:** each app's identifier (pass it to `erdo_connect_integration`), name, `auth_types`, and `source` (`native` or `pipedream`).

#### erdo\_connect\_integration

Connect an app. With `credentials`, credential-based apps are created, verified, and activated in this one call. Without credentials, OAuth apps return a `connect_url` for the user to authorize in a browser.

**Parameters:**

| Parameter     | Type      | Description                                                                                                                                                                                                                                                                                                   |
| ------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `app`         | string    | App identifier from `erdo_search_integration_apps`                                                                                                                                                                                                                                                            |
| `name`        | string    | Optional display name for the connection                                                                                                                                                                                                                                                                      |
| `credentials` | object    | Credentials for credential-based apps (e.g. database connection fields). Omit for OAuth apps.                                                                                                                                                                                                                 |
| `scopes`      | string\[] | Optional scopes to grant; defaults to all the app supports                                                                                                                                                                                                                                                    |
| `return_url`  | string    | Optional absolute `http(s)` URL to send the user's browser to after they authorize an OAuth app at `connect_url`. Defaults to Erdo's data page — pass your own page when embedding the connect flow in your product so the user lands back on it. Ignored for credential-based apps that connect in one call. |

**Returns:** `{ app, integration_id, status, connect_url?, next_step }` — `status` is `active` when connected immediately, `pending` when browser authorization is needed.

#### erdo\_check\_integration\_connection

Check whether an app is connected. For browser-authorized apps this also completes any connection the user finished since `erdo_connect_integration` was called — poll it after the user opens the `connect_url`.

**Parameters:**

| Parameter | Type   | Description                                             |
| --------- | ------ | ------------------------------------------------------- |
| `app`     | string | The app identifier passed to `erdo_connect_integration` |

#### erdo\_discover\_integration\_tables

Discover what a connected database integration exposes. Without `schema_name`, lists the selectable schemas (all SQL databases and warehouses); with it, lists that schema's tables with columns — supported for SQL databases (Postgres, MySQL, and compatible); warehouses (BigQuery, Snowflake, ClickHouse) list schemas but not per-table columns here. Use before `erdo_create_integration_dataset`.

**Parameters:**

| Parameter     | Type   | Description                                                               |
| ------------- | ------ | ------------------------------------------------------------------------- |
| `integration` | string | App key (e.g. `postgres`) or integration id from `erdo_list_integrations` |
| `schema_name` | string | Optional. Schema to list tables for.                                      |

#### erdo\_create\_integration\_dataset

Create a dataset backed by any connected integration that exposes queryable data. Database and warehouse integrations may query live; sync-capable API integrations automatically materialize their provider resources through Erdo's data platform. Integrations that expose selectable scopes require a `schemas` selection (some allow only one); integrations with neither a direct query path nor dataset sync are rejected. Resource/column discovery runs in the background and the schema appears after discovery or the first sync.

**Parameters:**

| Parameter     | Type      | Description                                 |
| ------------- | --------- | ------------------------------------------- |
| `integration` | string    | App key or integration id                   |
| `name`        | string    | Display name for the dataset                |
| `description` | string    | Optional. Description shown to agents.      |
| `schemas`     | string\[] | Selectable schema names (or ids) to include |

**Returns:** `{ dataset_id, slug, name, status }`

#### erdo\_configure\_integration\_dataset

Update an existing integration dataset using provider-declared segments rather than provider-specific campaign, account, or schema endpoints. Optionally enables the canonical data-platform sync; no snapshot dataset is created.

**Parameters:**

| Parameter     | Type      | Description                                                           |
| ------------- | --------- | --------------------------------------------------------------------- |
| `dataset_id`  | string    | Existing integration dataset id                                       |
| `segments`    | string\[] | Segment names or ids from `erdo_discover_integration_tables`          |
| `enable_sync` | boolean   | Optional. Enable canonical data-platform sync after saving the scope. |

**Returns:** `{ dataset_id, status, sync_enabled, segments }`

### Thread & Conversation Tools

#### erdo\_list\_threads

List conversation threads with name, creation date, and visibility.

**Parameters:**

| Parameter | Type   | Description                         |
| --------- | ------ | ----------------------------------- |
| `limit`   | number | Optional. Max results (default 20). |

#### erdo\_get\_thread\_messages

Get all messages from a conversation thread including content and metadata.
Content items preserve `ui_content_type` and `created_by_invocation_id` so MCP
clients can render the same tool activity and generated UI as Erdo.

**Parameters:**

| Parameter   | Type   | Description |
| ----------- | ------ | ----------- |
| `thread_id` | string | Thread UUID |

#### erdo\_create\_thread

Create a new conversation thread, optionally with datasets attached.

**Parameters:**

| Parameter     | Type      | Description                        |
| ------------- | --------- | ---------------------------------- |
| `name`        | string    | Optional. Thread name.             |
| `dataset_ids` | string\[] | Optional. Dataset UUIDs to attach. |

#### erdo\_send\_message

Send a message to a thread and get an AI-generated response. The message is processed by an AI agent that can analyze data, write SQL, generate charts, and more.

<Warning>
  This tool can take 30 seconds to 2 minutes depending on the question complexity.
</Warning>

**Parameters:**

| Parameter   | Type   | Description                                                                                                   |
| ----------- | ------ | ------------------------------------------------------------------------------------------------------------- |
| `thread_id` | string | Thread UUID                                                                                                   |
| `message`   | string | The message to send                                                                                           |
| `agent_key` | string | Optional. Agent to use (default: `erdo.data-question-answerer`). Use `erdo.data-analyst` for deeper analysis. |
| `timezone`  | string | Optional. User timezone (e.g. `America/New_York`).                                                            |

**Returns:** The thread ID, message ID, status, and the agent's answer.

### Knowledge Tools

[Knowledge](/knowledge) records store durable context (`snippet`) or reusable instructions (`skill`) that Erdo's agents read in future conversations. Use these to teach the workforce your definitions, rules, and procedures.

#### erdo\_create\_knowledge

Create a new Knowledge record or skill.

**Parameters:**

| Parameter     | Type      | Description                                                                      |
| ------------- | --------- | -------------------------------------------------------------------------------- |
| `title`       | string    | Short title for the record                                                       |
| `content`     | string    | The main content or instructions                                                 |
| `description` | string    | Brief description of what this record does                                       |
| `type`        | string    | `snippet` (knowledge) or `skill` (reusable instructions). Defaults to `snippet`. |
| `category`    | string    | Optional. Category (e.g. "Data Analysis", "SQL").                                |
| `tags`        | string\[] | Optional. Tags for organization.                                                 |
| `dataset_ids` | string\[] | Optional. Associated dataset UUIDs.                                              |

#### erdo\_search\_knowledge

Search Knowledge records and skills by semantic similarity.

**Parameters:**

| Parameter | Type   | Description                                       |
| --------- | ------ | ------------------------------------------------- |
| `query`   | string | Search text                                       |
| `limit`   | number | Optional. Max results (default 10, hard cap 100). |

#### erdo\_list\_knowledge

List Knowledge records and skills with optional filtering.

**Parameters:**

| Parameter  | Type   | Description                                       |
| ---------- | ------ | ------------------------------------------------- |
| `type`     | string | Optional. Filter by `snippet` or `skill`.         |
| `category` | string | Optional. Filter by category.                     |
| `limit`    | number | Optional. Max results (default 20, hard cap 100). |
| `offset`   | number | Optional. Pagination offset.                      |

#### erdo\_delete\_knowledge

Delete a Knowledge object by ID (soft delete — can be recovered).

**Parameters:**

| Parameter             | Type   | Description           |
| --------------------- | ------ | --------------------- |
| `knowledge_object_id` | string | Knowledge object UUID |

#### erdo\_set\_knowledge\_visibility

Set a Knowledge object's [visibility](/knowledge#visibility--workspace-vs-public): `workspace` keeps it organization-internal (the default), `public` opts it into anonymous external surfaces such as the [website voice widget](/voice-widget) — a draft is approved in the same step and goes live immediately. Only make non-sensitive, customer-facing facts public.

**Parameters:**

| Parameter             | Type   | Description             |
| --------------------- | ------ | ----------------------- |
| `knowledge_object_id` | string | Knowledge object UUID   |
| `visibility`          | string | `workspace` or `public` |

### KV (Collection) Tools

KV stores are Erdo's shared **key/value store** — named, org-level stores (also called *collections*) holding config and values (pricing, targets, brand tokens) that stay consistent everywhere. The same stores are read by [pages](/apps/build-apps#per-page-state-kv) (`erdo.kv`, aliased `erdo.collections`), referenced from [Knowledge](/knowledge#shared-values-with-collections) bodies as `{{slug.key}}`, and reachable from the agent runtime — one store, one source of truth. Values are any JSON type. Pages also get a private, lazily-provisioned KV store of their own; these tools operate on **named** stores shared across pages and knowledge.

#### erdo\_list\_kv\_stores

List the organization's named KV stores with their slug and item count.

**Parameters:** none.

#### erdo\_get\_kv\_item

Read one value from a named KV store by key. Use for shared config/metrics that should be consistent everywhere rather than hardcoding values.

**Parameters:**

| Parameter | Type   | Description          |
| --------- | ------ | -------------------- |
| `kv_slug` | string | Slug of the KV store |
| `key`     | string | Item key to read     |

**Returns:** `{ key, value, found }`.

#### erdo\_set\_kv\_item

Write one value (any JSON type) to a named KV store by key — the canonical value everything references. Requires write (EDIT) access on the store. Reference it from Knowledge prose as `{{kv_slug.key}}` so it stays current everywhere.

**Parameters:**

| Parameter | Type   | Description                                                |
| --------- | ------ | ---------------------------------------------------------- |
| `kv_slug` | string | Slug of the KV store to write to                           |
| `key`     | string | Item key to set                                            |
| `value`   | any    | Value to store — string, number, boolean, object, or array |

#### erdo\_create\_kv\_store

Create a named KV store — a shared key/value store for config and values referenced across pages and knowledge.

**Parameters:**

| Parameter | Type   | Description                                                    |
| --------- | ------ | -------------------------------------------------------------- |
| `slug`    | string | Slug for the new KV store (lowercase letters, digits, hyphens) |

#### erdo\_delete\_kv\_item

Delete one value from a named KV store by key. Requires write (EDIT) access on the store. Deleting a key that doesn't exist is a no-op.

**Parameters:**

| Parameter | Type   | Description                         |
| --------- | ------ | ----------------------------------- |
| `kv_slug` | string | Slug of the KV store to delete from |
| `key`     | string | Item key to delete                  |

### Artifact Tools

Artifacts are AI-generated outputs from agent runs and automations — insights, charts, metrics, alerts, and suggestions.

#### erdo\_list\_artifacts

List artifacts with optional type filtering.

**Parameters:**

| Parameter | Type   | Description                                                                        |
| --------- | ------ | ---------------------------------------------------------------------------------- |
| `type`    | string | Optional. Filter by: `insight`, `chart`, `metric`, `alert`, `table`, `suggestion`. |
| `limit`   | number | Optional. Max results (default 20).                                                |
| `offset`  | number | Optional. Pagination offset.                                                       |

#### erdo\_get\_artifact

Get full details of a specific artifact including its content, metadata, and severity.

**Parameters:**

| Parameter     | Type   | Description   |
| ------------- | ------ | ------------- |
| `artifact_id` | string | Artifact UUID |

### Media Tools

#### erdo\_screenshot

Capture a screenshot of a web page and get back a **signed, time-limited download URL** for the PNG, plus its dimensions. Use this when you need the image *file*.

By default it renders a **public** URL (a marketing site, a published Erdo page at `https://pages.erdo.ai/p/{id}`, a competitor page). Pass `instructions` to capture a page that requires **logging in first** — the capture runs asynchronously (returns a `job_id` with `status: "processing"`); poll `erdo_screenshot_result` with that `job_id` until it's `done`.

**Parameters:**

| Parameter             | Type    | Description                                                                                                                                                                             |
| --------------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `url`                 | string  | http(s) URL to capture.                                                                                                                                                                 |
| `instructions`        | string  | Optional. Steps to perform before the shot (e.g. sign in). Triggers an async capture.                                                                                                   |
| `full_page`           | boolean | Optional. Capture the whole scrollable page (default true); set `false` for just the viewport.                                                                                          |
| `width`               | number  | Optional. Viewport width in px (default 1440). Use \~390 for a phone.                                                                                                                   |
| `height`              | number  | Optional. Viewport height in px (default 900).                                                                                                                                          |
| `color_scheme`        | string  | Optional. `light`/`dark` — emulates the OS theme for **public** pages that follow system theme. Ignored for signed-in captures (use `local_storage`).                                   |
| `device_scale_factor` | number  | Optional. Pixel density 1–3 (default 2).                                                                                                                                                |
| `local_storage`       | object  | Optional. `localStorage` entries set before the page loads, then reloaded. The reliable way to theme a **signed-in** capture — pass `{"theme": "dark"}` to render the app in dark mode. |

Returns `signed_url`, `bucket_key`, `media_type`, `width`, `height`, and `expires_at` (or `job_id` + `status` for an instructed capture — fetch the result with `erdo_screenshot_result`).

#### erdo\_screenshot\_result

Poll an instructed (async) capture started by `erdo_screenshot`. Pass the `job_id` it returned.

**Parameters:**

| Parameter | Type   | Description                                                    |
| --------- | ------ | -------------------------------------------------------------- |
| `job_id`  | string | The `job_id` returned by an instructed `erdo_screenshot` call. |

Returns the same shape as `erdo_screenshot` once `status` is `done` (`signed_url`, `bucket_key`, `media_type`, dimensions, `expires_at`); `processing` means try again shortly, `error` includes an `error` message.

#### erdo\_upload\_image

Upload an image so it can be attached to a data question via `erdo_ask_data_question`. Pass the raw bytes as standard base64. Accepts PNG, JPEG, WEBP, or GIF, up to 5 MB.

**Parameters:**

| Parameter      | Type   | Description                                              |
| -------------- | ------ | -------------------------------------------------------- |
| `image_base64` | string | Standard base64-encoded image bytes.                     |
| `media_type`   | string | `image/png`, `image/jpeg`, `image/webp`, or `image/gif`. |

Returns `{ bucket_key, media_type, width, height }` — pass `bucket_key` in the `images` array of `erdo_ask_data_question`.

### Agent Run Tools

Agent runs are the record of what agents have done — the runs behind `erdo_ask_data_question`, `erdo_send_message`, and automations.

#### erdo\_list\_agent\_runs

List agent runs, filterable by agent or thread or status.

**Parameters:**

| Parameter   | Type   | Description                                                                                    |
| ----------- | ------ | ---------------------------------------------------------------------------------------------- |
| `agent_key` | string | Optional. Filter by agent, e.g. `erdo.artifact-builder` (mutually exclusive with `thread_id`). |
| `thread_id` | string | Optional. Filter to one thread (mutually exclusive with `agent_key`).                          |
| `limit`     | number | Optional. Max runs (default 50).                                                               |
| `offset`    | number | Optional. Pagination offset.                                                                   |

#### erdo\_get\_agent\_run

Get one agent run: status, agent, output, and trace metadata.

**Parameters:**

| Parameter | Type   | Description                                 |
| --------- | ------ | ------------------------------------------- |
| `run_id`  | string | Agent run id (from `erdo_list_agent_runs`). |

### Approval Tools

Approval requests are actions an agent paused on, awaiting a human decision. See [Approvals](/approvals).

#### erdo\_list\_approvals

List approval requests, filterable by status.

**Parameters:**

| Parameter         | Type   | Description                                                                              |
| ----------------- | ------ | ---------------------------------------------------------------------------------------- |
| `status`          | string | Optional. Filter by status: `pending`, `approved`, `rejected`, `expired` (default: all). |
| `limit`           | number | Optional. Max requests (default 50).                                                     |
| `offset`          | number | Optional. Pagination offset.                                                             |
| `workstream_slug` | string | Optional. Return only approvals attached to this exact Workstream or Strategy.           |

#### erdo\_decide\_approval

Approve or reject a pending approval request so the paused agent run can continue (or be rejected).

**Parameters:**

| Parameter  | Type   | Description                                                                                              |
| ---------- | ------ | -------------------------------------------------------------------------------------------------------- |
| `id`       | string | Approval request id (from `erdo_list_approvals`).                                                        |
| `decision` | string | `approved` or `rejected`.                                                                                |
| `scope`    | string | Optional. `once` (default), `always_this_job`, `always_this_workstream`, `always_org`, or `always_user`. |

### Page Deploy Tools

Deploy HTML pages/apps to Erdo from any coding agent. Pages run in the Erdo page runtime: `window.erdo` gives them governed access to the datasets you grant, and the default `react-tailwind` runtime provides React 18, Tailwind, and the Erdo UI components (`DatasetChart`, `DatasetTable`, ...). A deploy with validation errors still saves and reports them — fix with `erdo_update_page` and iterate until clean.

#### erdo\_deploy\_page

Deploy a new page and get back its URL plus structured validation results. Pages are private by default.

**Parameters:**

| Parameter                | Type      | Description                                                                                                                     |
| ------------------------ | --------- | ------------------------------------------------------------------------------------------------------------------------------- |
| `title`                  | string    | Page title shown in Erdo.                                                                                                       |
| `html`                   | string    | HTML content — full document or fragment. With `react-tailwind`, include a root element and put React code in `js`.             |
| `css`                    | string    | Optional stylesheet.                                                                                                            |
| `js`                     | string    | Optional JavaScript/JSX.                                                                                                        |
| `runtime`                | string    | Optional. `react-tailwind` (default) or `none`.                                                                                 |
| `dataset_slugs`          | string\[] | Optional. Datasets the page **reads** via `window.erdo.queryDataset` — granted read access on each.                             |
| `writable_dataset_slugs` | string\[] | Optional. Datasets the page may **append to** via `window.erdo.insertRows` — granted write access on each (you must hold edit). |
| `kv_slugs`               | string\[] | Optional. Named KV stores (collections) the page **reads** via `erdo.kv.get/list` — granted read access on each.                |
| `writable_kv_slugs`      | string\[] | Optional. Named KV stores the page may **write** via `erdo.kv.set/delete` — granted edit access on each.                        |
| `public`                 | boolean   | Optional. Make the page publicly viewable at its share URL immediately (default: private).                                      |

**Returns:** `{ id, title, url, public_url?, public, thread_id, validation }`. `url` is the authenticated editor view; `public_url` is the visitor-facing share link (present while public). Use the returned URLs verbatim — hosts differ per environment.

<Note>
  `dataset_slugs` / `kv_slugs` grant **read**; `writable_dataset_slugs` / `writable_kv_slugs` grant **write**. `erdo.insertRows` and writes to a named KV store only work when the matching writable grant was declared at deploy — otherwise they return a permission error. The page's own private KV store and `submitEvent` (pipelines) need no write grant. See [Build Apps](/apps/build-apps#auth--sharing).
</Note>

#### erdo\_update\_page

Update a deployed page. Provided fields are merged — send only `js` to fix a script without resending `html`/`css`. Returns fresh validation results.

**Parameters:**

| Parameter                | Type      | Description                                                   |
| ------------------------ | --------- | ------------------------------------------------------------- |
| `page_id`                | string    | Page ID from `erdo_deploy_page` or `erdo_list_artifacts`.     |
| `title`                  | string    | Optional. New title.                                          |
| `html`                   | string    | Optional. New HTML.                                           |
| `css`                    | string    | Optional. New stylesheet.                                     |
| `js`                     | string    | Optional. New JavaScript/JSX.                                 |
| `dataset_slugs`          | string\[] | Optional. Replacement read-dataset list.                      |
| `writable_dataset_slugs` | string\[] | Optional. Replacement writable-dataset list (`[]` clears it). |
| `kv_slugs`               | string\[] | Optional. Replacement read KV-store list.                     |
| `writable_kv_slugs`      | string\[] | Optional. Replacement writable KV-store list.                 |
| `public`                 | boolean   | Optional. `true` shares publicly, `false` reverts to private. |

#### erdo\_validate\_page

Dry-run validation without deploying anything: HTML structure, JS/JSX syntax and runtime smoke checks, `window.erdo` usage, and dataset-slug references. Real data queries are additionally probed on actual deploy/update.

**Parameters:** same content fields as `erdo_deploy_page` (`html`, `css`, `js`, `runtime`, `dataset_slugs`).

### Automation Tools

Heartbeats are recurring agents that analyze your data on a schedule and generate insights, alerts, and reports.

#### erdo\_list\_heartbeats

List heartbeat automations with their schedule, state, and latest execution status.

**Parameters:**

| Parameter | Type   | Description                         |
| --------- | ------ | ----------------------------------- |
| `limit`   | number | Optional. Max results (default 20). |
| `offset`  | number | Optional. Pagination offset.        |

#### erdo\_create\_heartbeat

Create a recurring automation that analyzes your data on a schedule.

**Parameters:**

| Parameter             | Type      | Description                                                       |
| --------------------- | --------- | ----------------------------------------------------------------- |
| `name`                | string    | Name for the automation                                           |
| `instructions`        | string    | Instructions for the agent to follow on each run                  |
| `interval_minutes`    | number    | How often to run (minimum 5 minutes)                              |
| `description`         | string    | Optional. What this automation does.                              |
| `timezone`            | string    | Optional. Timezone for scheduling (default UTC).                  |
| `active_window_start` | string    | Optional. Only run after this time (24h format, e.g. `09:00`).    |
| `active_window_end`   | string    | Optional. Only run before this time (e.g. `18:00`).               |
| `active_days`         | number\[] | Optional. Days of week to run (0=Sun..6=Sat). Omit for every day. |
| `dataset_ids`         | string\[] | Optional. Dataset UUIDs to analyze.                               |
| `effort`              | string    | Optional. Agent effort: `low`, `medium`, or `high`.               |

#### erdo\_run\_heartbeat

Manually trigger a heartbeat to run immediately, outside its normal schedule.

**Parameters:**

| Parameter      | Type   | Description    |
| -------------- | ------ | -------------- |
| `heartbeat_id` | string | Heartbeat UUID |

#### erdo\_set\_heartbeat\_state

Enable or disable a heartbeat automation. Set it to `disabled` to pause a misbehaving automation so it stops running, or `active` to resume it.

**Parameters:**

| Parameter      | Type   | Description            |
| -------------- | ------ | ---------------------- |
| `heartbeat_id` | string | Heartbeat UUID         |
| `state`        | string | `active` or `disabled` |

#### erdo\_list\_heartbeat\_executions

List recent executions of a heartbeat with status, timing, and associated thread.

**Parameters:**

| Parameter      | Type   | Description                         |
| -------------- | ------ | ----------------------------------- |
| `heartbeat_id` | string | Heartbeat UUID                      |
| `limit`        | number | Optional. Max results (default 10). |

## REST API

All MCP tools are also available as REST endpoints for direct HTTP integration. Use these when you don't need the full MCP protocol (e.g. from LangChain, Vercel AI SDK, or custom scripts).

**Base URL:** `https://api.erdo.ai`

**Authentication:** Pass `Authorization: Bearer YOUR_API_KEY` header. The organization is inferred from your API key.

### Endpoint Reference

#### Data Endpoints

| MCP Tool                      | REST Endpoint                 | Method |
| ----------------------------- | ----------------------------- | ------ |
| `erdo_list_datasets`          | `/v1/datasets`                | GET    |
| `erdo_search_datasets`        | `/v1/datasets-search`         | GET    |
| `erdo_get_dataset_schema`     | `/v1/datasets/:id/schema`     | GET    |
| `erdo_gather_dataset_context` | `/v1/dataset-context`         | GET    |
| `erdo_fetch_dataset_contents` | `/v1/datasets/:slug/fetch`    | POST   |
| `erdo_run_query`              | `/v1/datasets/:slug/query`    | POST   |
| `erdo_query_data`             | `/v1/datasets/:slug/query-nl` | POST   |
| `erdo_ask_data_question`      | `/v1/ask`                     | POST   |
| `erdo_render_chart`           | `/v1/render/chart`            | POST   |
| `erdo_render_table`           | `/v1/render/table`            | POST   |
| `erdo_screenshot`             | `/v1/screenshot`              | POST   |
| `erdo_screenshot_result`      | `/v1/screenshot/result`       | POST   |
| `erdo_upload_image`           | `/v1/images/upload`           | POST   |
| `erdo_create_dataset`         | `/v1/datasets-create`         | POST   |
| `erdo_upload_dataset_file`    | `/v1/datasets-upload`         | POST   |
| `erdo_delete_dataset`         | `/v1/datasets/:slug`          | DELETE |
| `erdo_write_rows`             | `/v1/datasets/:slug/rows`     | POST   |
| `erdo_delete_rows`            | `/v1/datasets/:slug/rows`     | DELETE |
| `erdo_update_dataset_schema`  | `/v1/datasets/:slug/schema`   | POST   |

#### Integration Endpoints

| MCP Tool                             | REST Endpoint                                    | Method |
| ------------------------------------ | ------------------------------------------------ | ------ |
| `erdo_list_integrations`             | `/v1/integrations`                               | GET    |
| `erdo_search_integration_apps`       | `/v1/integration-apps`                           | GET    |
| `erdo_connect_integration`           | `/v1/integrations-connect`                       | POST   |
| `erdo_check_integration_connection`  | `/v1/integrations-connect/:app`                  | GET    |
| `erdo_discover_integration_tables`   | `/v1/integrations/:integration/tables`           | GET    |
| `erdo_create_integration_dataset`    | `/v1/integration-datasets`                       | POST   |
| `erdo_configure_integration_dataset` | `/v1/integration-datasets/:dataset_id/configure` | POST   |

#### Thread & Conversation Endpoints

| MCP Tool                   | REST Endpoint              | Method |
| -------------------------- | -------------------------- | ------ |
| `erdo_list_threads`        | `/v1/threads`              | GET    |
| `erdo_get_thread_messages` | `/v1/threads/:id/messages` | GET    |
| `erdo_create_thread`       | `/v1/threads-create`       | POST   |
| `erdo_send_message`        | `/v1/threads/:id/send`     | POST   |

`erdo_send_message` accepts optional `context` for application state that should
guide this turn without being stored as the visible user message. Use `message`
for the operator's exact words and `context` for the current page, selection, or
other structured facts. The thread list includes the creator's identity and the
creation source; unnamed threads are titled from their first user message. Sent
messages retain the authenticated user's authorship in the transcript.
Message reads include that user's canonical ID, name, and email when the user
is still in the organization.

#### Knowledge Endpoints

| MCP Tool                        | REST Endpoint                  | Method |
| ------------------------------- | ------------------------------ | ------ |
| `erdo_create_knowledge`         | `/v1/knowledge`                | POST   |
| `erdo_search_knowledge`         | `/v1/knowledge-search`         | GET    |
| `erdo_list_knowledge`           | `/v1/knowledge`                | GET    |
| `erdo_delete_knowledge`         | `/v1/knowledge/:id`            | DELETE |
| `erdo_set_knowledge_visibility` | `/v1/knowledge/:id/visibility` | PATCH  |

#### KV (Collection) Endpoints

| MCP Tool               | REST Endpoint             | Method |
| ---------------------- | ------------------------- | ------ |
| `erdo_list_kv_stores`  | `/v1/kv`                  | GET    |
| `erdo_create_kv_store` | `/v1/kv`                  | POST   |
| `erdo_get_kv_item`     | `/v1/kv/:slug/items/:key` | GET    |
| `erdo_set_kv_item`     | `/v1/kv/:slug/items/:key` | PUT    |
| `erdo_delete_kv_item`  | `/v1/kv/:slug/items/:key` | DELETE |

#### Artifact Endpoints

| MCP Tool              | REST Endpoint       | Method |
| --------------------- | ------------------- | ------ |
| `erdo_list_artifacts` | `/v1/artifacts`     | GET    |
| `erdo_get_artifact`   | `/v1/artifacts/:id` | GET    |

#### Page Deploy Endpoints

| MCP Tool             | REST Endpoint        | Method |
| -------------------- | -------------------- | ------ |
| `erdo_deploy_page`   | `/v1/pages`          | POST   |
| `erdo_update_page`   | `/v1/pages/:id`      | PUT    |
| `erdo_validate_page` | `/v1/pages/validate` | POST   |

#### Automation Endpoints

| MCP Tool                         | REST Endpoint                   | Method |
| -------------------------------- | ------------------------------- | ------ |
| `erdo_list_heartbeats`           | `/v1/heartbeats`                | GET    |
| `erdo_create_heartbeat`          | `/v1/heartbeats`                | POST   |
| `erdo_run_heartbeat`             | `/v1/heartbeats/:id/run`        | POST   |
| `erdo_set_heartbeat_state`       | `/v1/heartbeats/:id/state`      | POST   |
| `erdo_list_heartbeat_executions` | `/v1/heartbeats/:id/executions` | GET    |

#### Agent Run Endpoints

| MCP Tool               | REST Endpoint     | Method |
| ---------------------- | ----------------- | ------ |
| `erdo_list_agent_runs` | `/v1/runs`        | GET    |
| `erdo_get_agent_run`   | `/v1/runs/:runID` | GET    |

#### Approval Endpoints

| MCP Tool               | REST Endpoint              | Method |
| ---------------------- | -------------------------- | ------ |
| `erdo_list_approvals`  | `/v1/approvals`            | GET    |
| `erdo_decide_approval` | `/v1/approvals/:id/decide` | POST   |

#### Manager Account Endpoints

Operate many client orgs from one credential — see [Manager accounts](/manager-accounts).

| MCP Tool                           | REST Endpoint                     | Method |
| ---------------------------------- | --------------------------------- | ------ |
| `erdo_list_managed_organizations`  | `/v1/managed-organizations`       | GET    |
| `erdo_create_managed_organization` | `/v1/managed-organizations`       | POST   |
| `erdo_revoke_managed_organization` | `/v1/managed-organizations/:slug` | DELETE |

Manager-key creation is REST/CLI/Platform only. It is intentionally not exposed as an MCP tool because
the raw non-expiring credential must remain on a human-driven surface.

Evals, Workstreams, and Experiments have their own REST endpoints — see [Evals](/evals), [Workstreams](/workstreams), and [Experiments](/experiments).

### Examples

```bash theme={null}
# List datasets
curl https://api.erdo.ai/v1/datasets?limit=5 \
  -H "Authorization: Bearer YOUR_API_KEY"

# Ask a data question
curl -X POST https://api.erdo.ai/v1/ask \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"question": "What were total sales last quarter?"}'

# Create a knowledge record
curl -X POST https://api.erdo.ai/v1/knowledge \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"title": "Revenue analysis", "content": "Always compare to YoY when analyzing revenue", "description": "Revenue analysis best practice", "type": "skill"}'

# Set a shared KV value (the canonical number everything references)
curl -X PUT https://api.erdo.ai/v1/kv/pricing/items/monthly \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"value": "$29"}'

# Read it back
curl https://api.erdo.ai/v1/kv/pricing/items/monthly \
  -H "Authorization: Bearer YOUR_API_KEY"

# Write rows to a dataset
curl -X POST https://api.erdo.ai/v1/datasets/my-org.metrics/rows \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"rows": [{"date": "2025-03-17", "revenue": 42300, "orders": 156}], "key_column": "date"}'

# Delete rows from a dataset
curl -X DELETE https://api.erdo.ai/v1/datasets/my-org.metrics/rows \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"key_column": "date", "keys": ["2025-03-17"]}'

# Create a heartbeat automation
curl -X POST https://api.erdo.ai/v1/heartbeats \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name": "Daily revenue check", "instructions": "Check revenue for anomalies", "interval_minutes": 60}'
```

## Scoped Tokens & External Users

When building your own app on top of Erdo, you'll want your end-users to interact with Erdo without giving them full access to your organization. **Scoped tokens** solve this — they restrict access to specific datasets and threads that you choose.

All tools work with scoped tokens. Each tool automatically scopes results to the resources the token has access to.

Create scoped tokens via the [TypeScript SDK](/ts-sdk/client) using `createToken()`:

```typescript theme={null}
const token = await erdo.createToken({
  datasetIds: ['dataset-uuid-1', 'dataset-uuid-2'],
  threadIds: ['thread-uuid-1'],
});

// Pass this token to your end-user's MCP client
const transport = new StreamableHTTPClientTransport(
  new URL('https://api.erdo.ai/mcp'),
  {
    requestInit: {
      headers: { 'Authorization': `Bearer ${token}` },
    },
  },
);
```

### How scoping works

| Tool category                                               | Scoped token behavior                                                                                                                                |
| ----------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Data tools** (list, search, query, render, write, delete) | Only datasets included in the token scope. Write/delete requires edit permission.                                                                    |
| **Thread tools** (list, read, create, send)                 | Only threads in the token scope + threads they create. New threads are private to the user.                                                          |
| **Knowledge tools** (create, search, list, delete)          | Users create personal Knowledge records (not org-wide). Search/list returns their own records + public ones. Delete only works on their own records. |
| **KV tools** (list, get, set, create)                       | Only KV stores the token holds access to. `set`/`create` require an EDIT grant.                                                                      |
| **Artifact tools** (list, get)                              | Only artifacts from their organization                                                                                                               |
| **Automation tools** (create, run, list)                    | Users create personal heartbeats (not visible to org). List/run only shows their own heartbeats.                                                     |

<Note>
  Scoped tokens are designed for your customers' end-users. For your own team members, use organization API keys which have full access to all tools and org-wide visibility.
</Note>

## Building Apps with Erdo MCP

Beyond AI assistants, you can integrate Erdo's MCP server into your own applications:

* **[Vercel AI SDK](/ts-sdk/vercel-ai-sdk)** — Connect any LLM to Erdo tools with rich chart and table rendering in React
* **REST API** (above) — Direct HTTP integration without MCP
* **Any MCP client** — Use the `Custom App` tab above to connect from TypeScript, Python, Go, or any language with an MCP client library
