diff --git a/developers/mcp-tools.mdx b/developers/mcp-tools.mdx index 7e4b7dd..3c8f4de 100644 --- a/developers/mcp-tools.mdx +++ b/developers/mcp-tools.mdx @@ -15,6 +15,8 @@ In the tables below, required arguments come first and optional ones follow in p - 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. +A column a [masking rule](/features/data-masking) matches comes back masked in every tool that returns rows, `browse_table`, `execute_query` and `export_data` included. The mask replaces the value in `rows[][]`; `columns[]` and `row_count` are unchanged. A filter or sort in the request still runs on the stored values. + 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 > MCP**. ## Tool index diff --git a/docs.json b/docs.json index 7d896d6..8c7d132 100644 --- a/docs.json +++ b/docs.json @@ -402,6 +402,7 @@ "security/privacy", "security/managed-updates", "features/safe-mode", + "features/data-masking", "features/acknowledgements" ] } diff --git a/features/data-masking.mdx b/features/data-masking.mdx new file mode 100644 index 0000000..f8ad8e7 --- /dev/null +++ b/features/data-masking.mdx @@ -0,0 +1,71 @@ +--- +title: Data Masking +description: Hide column values such as emails and card numbers in the grid, copies, exports, the AI assistant and MCP +--- + +Turn on masking before a screen share, a pairing session or a recorded demo on production data. A masked column still shows a value in every row, so you see that a column holds an email, or that two rows share one, without seeing the address. + +## Add a rule + +In the [connection form](/connections/connection-form#options), open the **Options** tab and find **Masking**. Click **Add Rule**, then set three things: + +| Field | What it takes | +|-------|---------------| +| **Match** | **Column** matches a column by name, **Type** matches its declared type | +| **Pattern** | `table.column` or `column`. `*` stands for any run of characters and `?` for one. Case does not matter | +| **Mask** | How the value shows, from the table that follows | + +Some patterns: + +| Pattern | Masks | +|---------|-------| +| `*.email` | Every column named `email`, in any table | +| `users.ssn` | `ssn` in the `users` table | +| `*password*` | Any column whose name contains `password` | +| `inet` with **Match** set to **Type** | Every column declared as `inet` | + + + Masking section of the connection form with a Column rule on *.email set to Email and a Column rule on users.ssn set to Last 4 Characters + Masking section of the connection form with a Column rule on *.email set to Email and a Column rule on users.ssn set to Last 4 Characters + + +When two rules match one column, the first in the list wins. Rules save with the connection, sync through [iCloud](/features/icloud-sync) and travel with a connection published to the [Team Catalog](/features/team). + +## Masks + +| Mask | `jane.doe@example.com` shows as | +|------|----------------------------------| +| **Full** | `••••` | +| **Last 4 Characters** | `••••.com` | +| **Email** | `j•••@example.com` | +| **Stable Hash** | 12 hex characters, the same for equal values | +| **NULL** | `NULL` | + +A value that is already NULL stays NULL. **Last 4 Characters** shows `••••` for a value of four characters or fewer, and **Email** shows `••••` for a value with no `@`. A **Stable Hash** stays the same until TablePro quits, so it matches equal values across the grid, an export and an MCP result from one session. + +## Where values are masked + +One rule covers every place a value leaves TablePro: + +- The data grid and a query tab's results, with an eye-slash icon in the header of each masked column. +- The row inspector, charts, **Copy**, **Copy As** and **Export Results**. +- **File > Export**, for every format. +- The AI assistant, both the results summary it reads and the queries it runs. +- Every [MCP](/features/mcp) tool that returns rows, and AppleScript and Shortcuts results. +- The referenced-row preview of a foreign key, Server Dashboard tiles and the [Routine Debugger](/features/routine-debugger) result grid. + +A query result has no source table for most engines, so a `table.column` rule matches a query result by its column name alone. `users.ssn` then also masks an `ssn` column a query reads from another table. + +## Editing and Reveal + +A grid that shows a masked column is read-only, since a mask cannot be written back or used to find its row. To see or edit the real values, right-click a masked column's header and choose **Reveal Masked Values…**. TablePro asks for Touch ID, or your macOS password on a Mac without it, and reloads the tab unmasked. A query tab whose statement is not a read reveals on its next run instead. + +Reveal lasts until the tab closes or opens another table. Other tabs, exports from other tabs, the AI assistant and MCP stay masked. + +## Limits + +Masking hides values inside TablePro. It does not replace database permissions: anyone with the connection's credentials can read the data with another client. + +- Filters, sorts and `WHERE` clauses run on the server against the stored values, so a filter on a masked column finds the real matches. +- A rule matches the column name a result reports. A query that renames or computes the column, such as `SELECT email AS contact`, returns it unmasked, and that includes queries an MCP client or the AI assistant writes. A **Type** rule catches a renamed column that keeps its type. +- **Copy To**, copying objects to another database and Compare move the stored values, not the masks. diff --git a/images/data-masking-dark.png b/images/data-masking-dark.png new file mode 100644 index 0000000..5b4e144 Binary files /dev/null and b/images/data-masking-dark.png differ diff --git a/images/data-masking.png b/images/data-masking.png new file mode 100644 index 0000000..6bfb01a Binary files /dev/null and b/images/data-masking.png differ