Skip to main content

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

FieldTypeRequiredDescription
sqlstringYesThe query text (1–100,000 chars).
default_databasestringNoDatabase used to qualify unqualified table identifiers.
default_schemastringNoSchema used to qualify unqualified table identifiers.
scenario_keystringNoCanonical scenario key stating the query's intent.
usestringNoFree-text intent (1–2,000 chars), mapped deterministically to one canonical scenario.
operationstringNoTransfer operation token, for example export.
destinationobjectNoTransfer destination; at least one of system or jurisdiction when present.
destination.systemstringNoDestination system token.
destination.jurisdictionstringNoDestination jurisdiction token.
consumer_jurisdictionstringNoJurisdiction 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:

FieldDescription
verdictpass (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).
findingsOne entry per referenced table, in reference order.
publicationCurrent-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. decision is the effective decision over the participating rows, decision_reason is the winner's reason, and instructions lists every participating instruction record, ranked winner first, each with its own decision_id for explain_why chaining. A governed reference where nothing published applies to this query's shape or intent is an explicit evaluated finding with decision: null and 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 carries reason_code: "no_published_instruction_state" and contributes warn; it is never a silent pass.
  • A conflicted reference carries the full conflict group with every opposing source and decision: 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

StateReason codesWhen
answeredEvery reference evaluated without conflict or review escalation; the verdict and findings stand on their own.
review_requiredconflicted_published_state, decision_requires_reviewSome 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_stateno_current_publication, scenario_unresolvedThe 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 pass is advisory — enforcement stays in your systems.
  • Only the current deployment publication is read; drafts and unpublished changes never affect the verdict.
  • SELECT 1 and other queries with zero table references are the one legitimate zero-findings pass, returned with publication: 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_why has no validation-record mode (kind: "validation" returns not_supported); chain the decision_id of any cited instruction instead.
  • Malformed input is invalid_parameters and is not metered; every accepted invocation, including one that ends in a parse error, consumes one metered MCP call.