Skip to content

Field Masking

Sensitive columns can be masked before results ever reach an MCP client — the AI agent sees a masked value, never the raw one. Rules are managed entirely in the Admin Panel under MCP Masking; no code changes or restart required.

Strategies

Strategy Example output Notes
hash a3f8bc2d1e4f9a07 SHA-256, truncated to 16 hex chars — same input always hashes the same, so it stays groupable/joinable in aggregate queries
partial j**@company.com Emails keep the first local-part character and the domain; other strings keep first/last character
redact [REDACTED] Full replacement, no information retained

NULL and empty-string values are always passed through unmasked (masking an absence of data is meaningless).

Rule Configuration

Each rule is a field_pattern + strategy + enabled toggle:

  • Pattern matching uses shell-style wildcards (fnmatch), e.g. *email*, *_phone, ssn
  • Case-insensitive — matched against the lowercased column name
  • Toggle on/off individually without deleting the rule
  • Rules are fetched fresh on every query (a local config-DB read, ~1ms) so changes apply to the very next call — no caching lag

Alias-Aware Matching

A naive implementation would only check the displayed column name — which an LLM-generated query can trivially rename (SELECT email AS contact_info), letting sensitive data slip past a rule targeting *email*.

SQLatte parses the generated SQL with sqlglot (dialect-aware — bigquery, mysql, postgres, or trino based on the connected provider) to resolve each output alias back to its real source column(s), including multi-column expressions like CONCAT(first_name, last_name). Masking rules are then matched against the source columns, not whatever alias the query happened to give them. If parsing fails for any reason, matching falls back to the display name — the same behavior as before this existed, never a hard failure.

SELECT email AS contact_info FROM users
                ↓ sqlglot resolves alias → source column
        contact_info → [email]
                ↓ rule "*email*" → partial
        contact_info: "j**@company.com"

Scope

Masking is applied identically in both local (stdio) and network (SSE) MCP modes, and only to MCP tool responses — it does not affect the chat UI or embedded widgets, which have their own access model.