> ## Documentation Index
> Fetch the complete documentation index at: https://ngquct-feat-saved-query-version-control.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# MCP Tools

> The 47 tools TablePro's MCP server exposes, with arguments, defaults, result shapes, and scope requirements

If a dedicated tool covers the job, it beats hand-written SQL. It quotes identifiers for the engine in front of it, applies the user's row limits, and runs on connections where raw execution is refused. All 47 publish a JSON Schema for input and output, so a client can validate both sides without reading this page. For the wire format, headers and error codes see [MCP Protocol](/external-api/mcp-protocol).

## How to read this page

In the tables below, required arguments come first and optional ones follow in parentheses. Unless a row says otherwise:

* `connection_id` is a connection UUID from `list_connections`.
* `database` and `schema` default to whatever the connection is currently browsing. Passing them targets somewhere else **without moving the app's selection**.
* An unknown argument is rejected with `-32602`. There is no silent ignoring.
* The result comes back twice: as JSON text in `content[0]`, and as a typed object in `structuredContent`. Read `structuredContent`.
* Timestamps are ISO 8601, error text is redacted, and "a result set" means `columns[]`, `rows[][]`, `row_count`, `rows_affected`, `execution_time_ms`, `is_truncated`, plus `status_message`, `database` and `schema` when they apply.

Row limits default to the server's **Default row limit** setting (500) and are capped by **Maximum row limit** (10,000). Timeouts default to **Query timeout** (30 seconds) and are capped at 300. Both live in **Settings > Integrations**.

## Tool index

| Group                                           | Tools                                                                                                                                                                                                                                                                   |
| ----------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [Connections](#connections)                     | `list_connections` `connect` `disconnect` `get_connection_status` `switch_database` `switch_schema`                                                                                                                                                                     |
| [Schema discovery](#schema-discovery)           | `list_databases` `list_schemas` `list_tables` `describe_table` `get_table_ddl` `search_schema` `list_indexes` `list_foreign_keys` `list_triggers` `get_view_definition` `list_routines` `list_types` `list_partitions` `get_table_statistics` `get_database_statistics` |
| [Reading data](#reading-data)                   | `browse_table` `count_rows` `execute_query` `explain_query` `export_data` `quote_identifiers`                                                                                                                                                                           |
| [Writing data](#writing-data)                   | `insert_rows` `confirm_destructive_operation` `transaction_control`                                                                                                                                                                                                     |
| [Databases and objects](#databases-and-objects) | `describe_create_database_options` `create_database` `drop_database_object` `list_maintenance_operations` `run_maintenance`                                                                                                                                             |
| [Server administration](#server-administration) | `get_server_dashboard` `stop_server_session` `list_principals` `list_grants` `list_session_contexts`                                                                                                                                                                    |
| [The user's workspace](#the-users-workspace)    | `list_favorite_tables` `list_recent_tables` `list_recent_tabs` `focus_query_tab` `open_connection_window` `open_table_tab` `search_query_history`                                                                                                                       |

## Scopes and gates

Fourteen tools need `tools:write`: `connect`, `disconnect`, `switch_database`, `switch_schema`, `insert_rows`, `confirm_destructive_operation`, `transaction_control`, `create_database`, `drop_database_object`, `run_maintenance`, `stop_server_session`, `focus_query_tab`, `open_connection_window`, `open_table_tab`. The other 33 need `tools:read`, `execute_query` among them until the statement writes.

Past the scope, a call clears three more gates:

1. **Connection allowlist.** A token limited to named connections gets `-33007` for anything else.
2. **Connection policy.** **External Clients** set to **Blocked** hides the connection: `list_connections` omits it and any tool that names it is refused. An AI policy of **Never** does the same. Otherwise the first call that touches a connection asks the user, as often as **Settings > Integrations > Approval** says. That question is always a dialog on the user's Mac, never an elicitation: a client that could answer it could also grant itself the connection. It has no deadline, so the client owns the timeout, and a refusal stands for 60 seconds before the question can be put again.
3. **Safe Mode.** A write goes through the connection's Safe Mode, which may ask the user to confirm it or to authenticate. See [Approvals](/external-api/mcp-protocol#approvals-and-input-required).

`confirm_destructive_operation` needs `admin` as well, so it takes a Full Access token, and **Read Only** refuses it outright whatever the token carries. No token skips the user's approval.

Statements that read or write files, or that run server-side code, are refused on every tool. So is more than one statement in a single call.

## Connections

| Tool                    | Arguments                   | Returns                                                                                                                                                                                                                  |
| ----------------------- | --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `list_connections`      | none                        | `connections[]` with `id`, `name`, `type`, `host`, `port`, `database`, `is_connected`, `ai_policy`, `external_access`, `safe_mode`. `is_connected` is false once a connection stops answering, before it is disconnected |
| `connect`               | `connection_id`             | `status` (`connected`), `connection_id`, `current_database`, plus `current_schema` and `server_version` when known. Returns once the driver is up                                                                        |
| `disconnect`            | `connection_id`             | `status` (`disconnected`), `connection_id`                                                                                                                                                                               |
| `get_connection_status` | `connection_id`             | `status` (`connected`, `connecting`, `disconnected`, `error`), `connection_id`, `current_database`, plus `current_schema`, `server_version`, `connected_at`, `last_active_at` and `error` when they apply                |
| `switch_database`       | `connection_id`, `database` | `status` (`switched`), `connection_id`, `current_database`                                                                                                                                                               |
| `switch_schema`         | `connection_id`, `schema`   | `status` (`switched`), `connection_id`, `current_schema`                                                                                                                                                                 |

`list_connections` omits connections blocked for external clients, connections whose AI policy is **Never**, and connections outside the token's allowlist. Its `database` is the session's browsed database when connected, the saved default otherwise.

The two `switch_` tools move what the user sees in TablePro. To run one statement elsewhere, pass `database` or `schema` to that tool instead.

## Schema discovery

| Tool                      | Arguments                                                    | Returns                                                                                                                                                                                              |
| ------------------------- | ------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `list_databases`          | `connection_id`                                              | `databases[]`, sorted                                                                                                                                                                                |
| `list_schemas`            | `connection_id` (`database`)                                 | `schemas[]`, `database`                                                                                                                                                                              |
| `list_tables`             | `connection_id` (`database`, `schema`, `include_row_counts`) | `tables[]` (`name`, `type`, `schema`, `comment`, `row_count`), `database`, `schema`, `row_counts_included`, `row_counts_are_approximate`                                                             |
| `describe_table`          | `connection_id`, `table` (`database`, `schema`)              | `table`, `database`, `schema`, `columns[]`, `indexes[]`, `foreign_keys[]`, `ddl`, `approximate_row_count`                                                                                            |
| `get_table_ddl`           | `connection_id`, `table` (`database`, `schema`)              | `table`, `schema`, `ddl`                                                                                                                                                                             |
| `search_schema`           | `connection_id`, `term` (`limit`, `database`, `schema`)      | `term`, `matches[]` (`kind` is `table` or `column`, plus `name`, `table`, `schema`, `object_type`, `data_type`), `is_truncated`. Table matches first                                                 |
| `list_indexes`            | `connection_id` (`table`, `database`, `schema`)              | `database`, `schema`, `tables[]` of `{ table, indexes[] }`. Tables with no index are left out                                                                                                        |
| `list_foreign_keys`       | `connection_id` (`table`, `database`, `schema`)              | `database`, `schema`, `tables[]` of `{ table, foreign_keys[] }`                                                                                                                                      |
| `list_triggers`           | `connection_id` (`table`, `database`, `schema`)              | `triggers[]` (`name`, `table`, `schema`, `timing`, `event`, `orientation`, `statement`, `definition`, `is_enabled`), sorted by table then name, plus `table` when one was named                      |
| `get_view_definition`     | `connection_id`, `view` (`database`, `schema`)               | `view`, `schema`, `definition`                                                                                                                                                                       |
| `list_routines`           | `connection_id` (`kind`, `database`, `schema`)               | `routines[]` (`name`, `kind`, `schema`, `qualified_name`, `signature`, `return_type`, `language`)                                                                                                    |
| `list_types`              | `connection_id` (`kind`, `database`, `schema`)               | `types[]` (`name`, `kind`, `schema`, `qualified_name`, `labels`, `fields`, `base_type`, `definition`), sorted by qualified name                                                                      |
| `list_partitions`         | `connection_id`, `table` (`database`, `schema`)              | `table`, `partitions[]` with `name` and `type` as in `list_tables`, plus `bound`, `ordinal_position`, `row_count`, `is_separate_relation`, `is_subpartitioned` and `parent_partition`                |
| `get_table_statistics`    | `connection_id`, `table` (`database`, `schema`)              | `table` plus whatever the engine records: `data_size_bytes`, `index_size_bytes`, `total_size_bytes`, `average_row_length`, `row_count`, `comment`, `engine`, `collation`, `created_at`, `updated_at` |
| `get_database_statistics` | `connection_id` (`database`)                                 | `databases[]` (`name`, `table_count`, `size_bytes`, `is_system_database`), sorted by name                                                                                                            |

`describe_table` is the one call to make before writing SQL against an unfamiliar table. A column always carries `name`, `data_type`, `is_nullable` and `is_primary_key`, and picks up `is_generated`, `default_value`, `extra`, `comment` and `allowed_values` where the engine reports them; indexes and foreign keys work the same way.

`include_row_counts` defaults to `false`. Counts come from engine statistics rather than `COUNT(*)`, and are fetched one table at a time, so `list_tables` skips them when the schema holds more than 200 objects.

`search_schema` locates a column without describing every table; its `limit` runs 1 to 500, default 50. `list_routines` takes `kind` as `procedure` or `function`, omitted for both, and its `signature` is the argument list, not the return type. `list_triggers` takes `table` to scope to one table, omitted for every trigger in the schema. `list_types` takes `kind` as `enum`, `composite`, `domain` or `range`, omitted for all four; `labels` is set for an enum, `fields` for a composite and `base_type` for a domain or range, and only PostgreSQL and PGlite answer with rows.

## Reading data

| Tool                | Arguments                                                                                                                    | Returns                                                                                                                                        |
| ------------------- | ---------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| `browse_table`      | `connection_id`, `table` (`columns`, `filters`, `logic`, `sort`, `limit`, `offset`, `timeout_seconds`, `database`, `schema`) | A result set                                                                                                                                   |
| `count_rows`        | `connection_id`, `table` (`exact`, `filters`, `logic`, `database`, `schema`)                                                 | `table`, `row_count`, `is_approximate`, `filter_count`                                                                                         |
| `execute_query`     | `connection_id`, `query` (`max_rows`, `timeout_seconds`, `database`, `schema`)                                               | A result set                                                                                                                                   |
| `explain_query`     | `connection_id`, `query` (`analyze`, `variant`, `timeout_seconds`, `database`, `schema`)                                     | `statement` as actually sent, `execution_time_ms`, `columns[]`, `rows[][]`, and where available `plan_text`, `plan` and `available_variants[]` |
| `export_data`       | `connection_id`, `format` (`query` **or** `tables`, `sql_table`, `output_path`, `max_rows`, `database`, `schema`)            | `format`, `rows_exported`, `is_truncated`, `path` when a file was written, and `exports[]` (`label`, `row_count`, `is_truncated`, `data`)      |
| `quote_identifiers` | `connection_id` and at least one of `identifiers`, `literals` (`database`, `schema`)                                         | `identifiers[]` as `{ input, quoted }` and `literals[]` as `{ input, escaped }`, in the order supplied                                         |

### `browse_table`

A filter is `{ column, operator, value, second_value, case_sensitive }`. `column` and `operator` are required, `second_value` is the upper bound for `BETWEEN`, and `case_sensitive` defaults to whatever the operator normally does. `logic` is `and` (default) or `or`. The operator list is TablePro's own filter vocabulary, published in the tool's input schema as an `enum`; read it from `tools/list` rather than hardcoding it. Raw SQL filters are not available over MCP.

A sort entry is `{ column, direction }`, `direction` being `ascending` (default) or `descending`, and entries apply in the order given. `offset` defaults to 0.

On an engine that cannot skip rows, such as Cloudflare R2 SQL, a nonzero `offset` is refused as an invalid argument and `limit` is lowered to the engine's maximum. The result echoes the `limit` actually used and sets `is_truncated` when the rows reached it.

Sorting on a column left out of `columns` appends it to the result. The columns you asked for keep the positions you asked for, and the sort column follows them, because some drivers can only order by a column they select.

### `count_rows`

Without filters this returns the engine's fast estimate unless `exact` is set. With filters it always counts for real, because an estimate cannot answer a filtered question.

### `execute_query`

One statement, 100 KB at most. `DROP` and `TRUNCATE` are refused here; use `confirm_destructive_operation`. When the request carries a `progressToken`, the tool emits progress at 0.0 (resolving), 0.3 (executing) and 1.0 (done).

### `explain_query`

Pass the query with no `EXPLAIN` prefix. Without `variant` the engine's first variant runs, and every result lists them in `available_variants[]`. `analyze: true` picks the variant that runs the statement, so an analyzed write needs `tools:write` and Safe Mode approval, and it is refused when the engine has no such variant or the `variant` you pass only estimates. An engine missing from the [support table](/features/explain-visualization#database-support) is refused.

### `export_data`

Formats are `csv`, `json` and `sql`; pass exactly one of `query` or `tables[]`. Without `output_path` the text comes back inline in each export entry. With it, the file lands in the user's Downloads folder and never overwrites, so a name that is taken gets `-1`, `-2` and so on; the path must stay inside Downloads, end in the format's extension, and not name a hidden file. SQL output uses the connection's own quoting and literal rules, so exporting a query needs `sql_table` to name the target table.

## Writing data

| Tool                            | Arguments                                                          | Returns                                                  |
| ------------------------------- | ------------------------------------------------------------------ | -------------------------------------------------------- |
| `insert_rows`                   | `connection_id`, `table`, `columns`, `rows` (`database`, `schema`) | `table`, `rows_submitted`, `rows_affected`               |
| `confirm_destructive_operation` | `connection_id`, `query` (`timeout_seconds`, `database`, `schema`) | A result set                                             |
| `transaction_control`           | `connection_id`, `action` (`database`, `schema`)                   | `status`, the action that completed, and `connection_id` |

`insert_rows` binds parameters, so values are never spliced into SQL text. Each row is an array with one value per column, and a call carries at most 1,000 rows.

`confirm_destructive_operation` runs one `DROP`, `TRUNCATE` or `ALTER … DROP`. Anything that is not destructive is rejected; use `execute_query` for those. The user approves it first, through your elicitation prompt if your client supports elicitation, otherwise through TablePro's own dialog on their Mac.

`transaction_control` takes `action` as `begin`, `commit` or `rollback`. The transaction stays open across calls until it is committed or rolled back, and it runs on the same session as the user's own tabs, so their editor runs and their grid saves land inside it while it is open. Leave nothing open.

On Redis the three actions are `MULTI`, `EXEC` and `DISCARD`. Every command between `begin` and `commit` answers `QUEUED` and does nothing until `commit` runs them, and `commit` fails naming each command the server then refused. `rollback` drops the block whole.

## Databases and objects

| Tool                               | Arguments                                                               | Returns                                                                   |
| ---------------------------------- | ----------------------------------------------------------------------- | ------------------------------------------------------------------------- |
| `describe_create_database_options` | `connection_id`                                                         | `is_supported`, `fields[]` (`key`, `label`, `default_value`, `options[]`) |
| `create_database`                  | `connection_id`, `name` (`options`)                                     | `status` (`created`), `database`                                          |
| `drop_database_object`             | `connection_id`, `kind`, `name`                                         | `status` (`dropped`) and `database` or `schema`                           |
| `list_maintenance_operations`      | `connection_id`                                                         | `operations[]` and `is_supported`                                         |
| `run_maintenance`                  | `connection_id`, `operation` (`table`, `options`, `database`, `schema`) | `operation`, `statements[]`, `results[]`, one result set per statement    |

`create_database` takes `options` as a flat object of string values drawn from `describe_create_database_options`, and asks the user before it runs. `drop_database_object` takes `kind` as `database` or `schema`, deletes everything inside, cannot be undone, and is approved first.

`run_maintenance` takes an `operation` name from `list_maintenance_operations` (`VACUUM`, `ANALYZE`, `OPTIMIZE` and whatever else the engine supports) and omits `table` for the whole database. TablePro generates the statements and runs them in order, asking for approval before each one.

<Info>
  `describe_create_database_options`, `list_maintenance_operations` and `list_session_contexts` answer `is_supported: false` on an engine that has no such concept rather than failing, and `list_partitions` answers with an empty list. Everything else that an engine cannot do comes back as an `unsupported` tool error.
</Info>

## Server administration

| Tool                    | Arguments                              | Returns                                                                                                                                                                   |
| ----------------------- | -------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `get_server_dashboard`  | `connection_id` (`panels`)             | The requested panels: `sessions`, `metrics`, `slow_queries`, plus `errors` for any the server could not answer                                                            |
| `stop_server_session`   | `connection_id`, `process_id` (`mode`) | The result set the engine produced                                                                                                                                        |
| `list_principals`       | `connection_id`                        | `principals[]` (`name`, `host`, `is_role`, `can_login`, `member_of[]`, `connection_limit`, `comment`, and `attributes[]` of `{ key, label, is_enabled }`), sorted by name |
| `list_grants`           | `connection_id`, `principal` (`host`)  | `principal`, `grants[]` (`privilege`, `scope`, `is_grantable`). `scope` is a dotted path, `*` for a server-wide grant                                                     |
| `list_session_contexts` | `connection_id`                        | `is_supported`, `contexts[]` (`id`, `label`, `value`, `options[]`)                                                                                                        |

`get_server_dashboard` reads the live panels TablePro shows, and runs on PostgreSQL, MySQL, SQL Server, ClickHouse, DuckDB and SQLite. Name the panels in `panels[]` or omit it for all three. A session carries `id` (the process id `stop_server_session` takes), `user`, `database`, `state`, `duration_seconds`, `query`, `can_kill` and `can_cancel`; a metric carries `id`, `label`, `value`, `unit`; a slow query carries `duration`, `query`, `user`, `database`. A panel the server could not answer is left out, and `errors` names it: one fixed sentence per panel, keyed by panel name. The server's own message is not sent to the client; it goes to the app's log, where the user can read it. When every requested panel fails, the call fails.

`stop_server_session` takes `mode` as `cancel` (default) to stop the running query or `kill` to end the session. The user always approves this one, whatever Safe Mode says.

`list_session_contexts` reports session-level contexts the engine exposes, such as a Snowflake warehouse or role, with the value the session currently holds.

## The user's workspace

These read and drive the TablePro app itself, and none of them bypasses the connection allowlist or a blocked connection.

| Tool                     | Arguments                                                      | Returns                                                                                                                                                                                                    |
| ------------------------ | -------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `list_favorite_tables`   | `connection_id`                                                | `favorites[]` (`name`, `database`, `schema`), sorted                                                                                                                                                       |
| `list_recent_tables`     | `connection_id`                                                | `recent_tables[]` (`name`, `database`, `schema`, `is_view`, `opened_at` as Unix epoch seconds), newest first                                                                                               |
| `list_recent_tabs`       | (`connection_id`, `limit`)                                     | `tabs[]` (`connection_id`, `connection_name`, `tab_id`, `tab_type`, `display_title`, `is_active`, plus `table_name`, `database_name`, `schema_name` and `window_id` when known)                            |
| `focus_query_tab`        | `tab_id`                                                       | `status` (`focused`), `tab_id`, `connection_id`, `window_id`                                                                                                                                               |
| `open_connection_window` | `connection_id`                                                | `status` (`opened`), `connection_id`, `tab_id`, `window_id`, `is_connected`                                                                                                                                |
| `open_table_tab`         | `connection_id`, `table_name` (`database_name`, `schema_name`) | `status` (`opened`), `connection_id`, `table_name`, `tab_id`, `window_id`                                                                                                                                  |
| `search_query_history`   | `query` (`connection_id`, `limit`, `since`, `until`)           | `entries[]` carrying `id`, `query`, the connection and database it ran on, `source`, `statement_type`, `executed_at`, `execution_time_ms`, `row_count`, `was_successful` and `error_message`, newest first |

Starred tables are a good hint at what matters in a database. `list_recent_tabs` covers every readable connection when `connection_id` is omitted, its `limit` runs 1 to 500, default 20, and `tab_type` is one of `query`, `table`, `createTable`, `erDiagram`, `serverDashboard`, `insights`, `usersRoles`.

`open_connection_window` and `open_table_tab` connect first if they have to, and return once the window has a tab or fail after 8 seconds.

`search_query_history` searches the history TablePro keeps on this Mac. Pass `""` to match everything and filter by date alone; `since` and `until` are Unix epoch seconds, and `limit` runs 1 to 500, default 50. Results cover only connections this client may reach, with or without `connection_id`.

## Annotations

Every tool ships MCP annotations so a client can decide what to auto-run:

* `readOnlyHint: true` on the 29 tools that only read. `execute_query` is not one of them.
* `destructiveHint: true` on `confirm_destructive_operation`, `drop_database_object` and `stop_server_session`.
* `idempotentHint: true` where repeating the call is safe.
* `openWorldHint: true` on the 14 that can reach the database server: every write except `switch_database`, `switch_schema` and `focus_query_tab`, plus `execute_query`, `explain_query` and `export_data`.

## Errors

A tool that fails returns a successful JSON-RPC result with `isError: true` and a text body starting with a code: `invalid_argument`, `not_connected`, `not_found`, `denied`, `timeout`, `unsupported`, `query_failed`, `user_cancelled`, `internal_failure`.

Protocol errors, with a JSON-RPC `error` envelope, are reserved for the request itself: a missing scope, an unknown tool, a bad header. The full table is in [MCP Protocol](/external-api/mcp-protocol#errors).
