validate_query_context
validate_query_context answers whether a SQL query is safe to run under your published governance. The server parses the SQL, resolves every referenced table, evaluates each reference against the current deployment publication's deployed instruction decisions, and returns a pass / warn / fail verdict with a per-reference finding for each table. It is read-only and advisory — the tool never executes, rewrites, or blocks the query; it describes what your published decisions say about it. Protocol alias: validate-query-context.
When to useDirect link to When to use
Call it as a pre-flight check whenever an agent is about to run or ship SQL — an analytics query, a pipeline transformation, a query embedded in code under review. It complements authorize_use: validate when you hold actual SQL and want every referenced asset checked at once, authorize when the question is a purpose against one asset. Stating your intent via scenario_key or use makes the verdict purpose-aware; adding transfer context makes transfer rules answer for a specific destination.
InputDirect link to Input
| Field | Type | Required | Description |
|---|---|---|---|
sql | string | Yes | The query text (1–100,000 chars). |
default_database | string | No | Database used to qualify unqualified table identifiers. |
default_schema | string | No | Schema used to qualify unqualified table identifiers. |
scenario_key | string | No | Canonical scenario key stating the query's intent. |
use | string | No | Free-text intent (1–2,000 chars), mapped deterministically to one canonical scenario. |
operation | string | No | Transfer operation token, for example export. |
destination | object | No | Transfer destination; at least one of system or jurisdiction when present. |
destination.system | string | No | Destination system token. |
destination.jurisdiction | string | No | Destination jurisdiction token. |
consumer_jurisdiction | string | No | Jurisdiction of the data consumer. |
Inputs are strict — unknown keys are rejected. SQL that cannot be parsed returns a typed query_parse_failed error, and a table identifier that cannot be fully qualified returns query_unresolved_identifier (provide default_database and default_schema); in both cases the query was not validated — parse problems are never converted into an empty pass. An intent that cannot be mapped to exactly one canonical scenario returns the typed scenario_unresolved answer rather than a guess; when transfer context is present and the use text maps to nothing, the intent defaults to residency.cross_border_transfer.
OutputDirect link to Output
The response is one of the three typed answer states, always with advisory: true. Both answered and review_required carry the closed verdict, the findings, and publication provenance:
| Field | Description |
|---|---|
verdict | pass (only allow or log-only effects, or no table references at all), warn (masking, review, retention, or conditional obligations apply, a reference is conflicted, or a reference has no published instruction state), or fail (a referenced asset is effectively denied). |
findings | One entry per referenced table, in reference order. |
publication | Current-publication provenance; null only for the zero-reference pass, where nothing was read. |
Each finding contains the table ref (column is always null — findings are table-scoped), a status, and the evidence:
status: "evaluated"— the reference was resolved and judged.decisionis the effective decision over the participating rows,decision_reasonis the winner's reason, andinstructionslists every participating instruction record, ranked winner first, each with its owndecision_idforexplain_whychaining. A governed reference where nothing published applies to this query's shape or intent is an explicit evaluated finding withdecision: nulland no instructions — a pass contribution.status: "not_enough_published_state"— the reference has no published instruction state at all (or is outside the current catalog). The finding carriesreason_code: "no_published_instruction_state"and contributeswarn; it is never a silent pass.- A conflicted reference carries the full
conflictgroup with every opposing source anddecision: null.
Which rows participate in a reference's verdict is intent- and column-aware:
- Column-level rows participate only when the query references that column or projects
*. Column extraction is conservative and over-inclusive by design: every identifier that is not a keyword, function name, or table reference counts as a possible column, and*counts only in projection positions — so an alias that shadows a governed column makes the verdict more cautious, never less. - Table-level rows participate when they match the stated intent scenario. Without intent, only always-applicable read controls (
access.read) participate — purpose, AI, and retention scenarios encode intents an unqualified query cannot assert. - Transfer-governance rows participate when transfer context is present, evaluated per destination exactly as in
authorize_use.
The per-reference verdict aggregates over every participating row — a participating deny fails the query even when a more specific column-level row outranks it as the citation winner. Ranking follows the standard order: specificity, then priority, then restrictiveness (deny > mask_full > mask_partial > require_review > conditional > retain > log_only > allow), then a stable key.
Answer statesDirect link to Answer states
| State | Reason codes | When |
|---|---|---|
answered | — | Every reference evaluated without conflict or review escalation; the verdict and findings stand on their own. |
review_required | conflicted_published_state, decision_requires_review | Some reference's applicable rows conflict (that finding carries all opposing sources), or some reference's effective decision is an explicit require_review. Findings and the verdict are still returned, plus next_actions. |
not_enough_published_state | no_current_publication, scenario_unresolved | The query references tables but no current publication exists, or the stated intent could not be mapped to a canonical scenario. |
Reference-level gaps surface inside findings as not_enough_published_state findings; only a missing publication or unresolved intent escalates to a whole-query not_enough_published_state answer.
ExampleDirect link to Example
A query joining a governed table to an ungoverned one, with a stated intent:
{
"sql": "SELECT c.customer_name, c.email FROM customers c JOIN legacy_customer_backup b ON b.customer_id = c.customer_id",
"default_database": "acmecloud_demo",
"default_schema": "public",
"scenario_key": "purpose.allowed_use"
}
The governed reference warns on the masked email column; the ungoverned reference is an explicit gap:
{
"state": "answered",
"advisory": true,
"verdict": "warn",
"findings": [
{
"ref": { "database": "acmecloud_demo", "schema": "public", "table": "customers", "column": null },
"status": "evaluated",
"reason_code": null,
"decision": "mask_partial",
"decision_reason": "acme-email-masking v1 masking:spec.accessControl.masking → mask_partial on acmecloud_demo.public.customers.email",
"instructions": [
{
"decision_id": "accef000-0000-4000-8000-000000000002",
"instruction_key": "masking:spec.accessControl.masking",
"instruction_family": "masking",
"scenario_key": "masking.display",
"scenario_lane": "platform",
"category": "enforceable",
"priority": 2,
"weight": 1,
"enforcement_mode": "enforce",
"decision": "mask_partial",
"decision_reason": "acme-email-masking v1 masking:spec.accessControl.masking → mask_partial on acmecloud_demo.public.customers.email",
"parameters": { "type": "partial", "exemptRoles": ["DATA_STEWARD", "PRIVACY_ADMIN"] },
"scope": { "database": "acmecloud_demo", "schema": "public", "table": "customers", "column": "email" },
"provenance": {
"policy_id": "accef000-0000-4000-8000-000000000003",
"policy_version_id": "accef000-0000-4000-8000-000000000004",
"policy_name": "AcmeCloud email masking (taxonomy)",
"version_number": 1
},
"primary_resolution_source": "taxonomy",
"resolution_paths": [{ "source": "taxonomy", "ref": "pii.contact.email" }],
"conflict_status": "none",
"conflict_group_id": null,
"conflicts_with": []
},
{
"decision_id": "accef000-0000-4000-8000-000000000020",
"instruction_key": "usage_guidance:spec.usage.permittedUses:permitted",
"instruction_family": "usage_guidance",
"scenario_key": "purpose.allowed_use",
"scenario_lane": "human",
"category": "advisory",
"priority": 2,
"weight": 1,
"enforcement_mode": "enforce",
"decision": "allow",
"decision_reason": "acme-customer-use v1 usage_guidance:spec.usage.permittedUses:permitted → allow on acmecloud_demo.public.customers",
"parameters": { "kind": "permitted", "uses": ["analytics", "reporting", "support"] },
"scope": { "database": "acmecloud_demo", "schema": "public", "table": "customers", "column": null },
"provenance": {
"policy_id": "accef000-0000-4000-8000-000000000006",
"policy_version_id": "accef000-0000-4000-8000-000000000007",
"policy_name": "AcmeCloud customer use guardrails",
"version_number": 1
},
"primary_resolution_source": "selector",
"resolution_paths": [{ "source": "selector", "ref": null }],
"conflict_status": "none",
"conflict_group_id": null,
"conflicts_with": []
}
],
"conflict": null
},
{
"ref": { "database": "acmecloud_demo", "schema": "public", "table": "legacy_customer_backup", "column": null },
"status": "not_enough_published_state",
"reason_code": "no_published_instruction_state",
"decision": null,
"decision_reason": null,
"instructions": [],
"conflict": null
}
],
"publication": {
"publication_id": "accef000-0000-4000-8000-000000000001",
"published_at": "2026-07-16T00:00:00.000Z"
}
}
The same query selecting only region and aggregated arr would pass — the masked column row participates only when email is actually referenced.
Limits and notesDirect link to Limits and notes
- The tool validates; it never runs the query, and a
passis advisory — enforcement stays in your systems. - Only the current deployment publication is read; drafts and unpublished changes never affect the verdict.
SELECT 1and other queries with zero table references are the one legitimate zero-findings pass, returned withpublication: null.- Column extraction works from the SQL text, not your live schema, so it can only over-include; a governed column the query truly reads is never missed.
- Findings are table-scoped; there is no per-column finding row — column evidence appears through the participating column-level instructions.
explain_whyhas no validation-record mode (kind: "validation"returnsnot_supported); chain thedecision_idof any cited instruction instead.- Malformed input is
invalid_parametersand is not metered; every accepted invocation, including one that ends in a parse error, consumes one metered MCP call.
RelatedDirect link to Related
authorize_use— purpose-level authorization for one assetexplain_why— evidence for any citeddecision_idinspect_data_meaning— masking and classification facts behind warn verdicts- Typed answer states and Scenarios
- MCP tool schemas — full input/output schemas