MCP server overview
Every Metatate Cloud workspace exposes a hosted MCP (Model Context Protocol) server. It gives AI agents — coding assistants, analytics copilots, review bots — governed answers about your data at the point of use: what an asset means, which rules apply, whether a proposed use is allowed, for what purpose, and why. Instead of pasting policy documents into prompts, an agent asks Metatate and gets a typed answer derived from what your workspace has actually published, with the evidence cited.
The server speaks MCP over HTTP (POST /mcp) and authenticates with a workspace access token. See Connect an agent for setup, or the Claude Code plugin for a packaged integration.
The nine toolsDirect link to The nine tools
| Tool | What it answers | Required scope |
|---|---|---|
discover_context | Which assets are governed, with per-asset instruction counts and the canonical scenario keys that are valid in follow-up calls. Filterable and paginated. | read |
get_decision_context | The full decision picture for an asset: every applicable instruction ranked, the effective decision, and published business context. | read |
inspect_data_meaning | What a table or column means: classification, sensitivity, PII, and masking facts. Facts only — not a decision. | read |
inspect_governance_rules | The active governance rules for an asset: instructions with their conditions, obligations, and policy provenance. | read |
authorize_use | Whether a proposed use of an asset is allowed, denied, or conditional — purpose-aware, actor-aware, and destination-aware. | read |
validate_query_context | A pass / warn / fail verdict for a SQL query: the query is parsed and every referenced asset is evaluated individually. | read |
explain_why | The evidence behind a prior decision — a published rule, a recorded authorization, or a recorded validation. | read |
request_access | Submits a durable steward review request citing a prior authorize_use answer. This is the one tool that writes. | request |
check_request | The status of a request this token submitted, and the granted exception when there is one. | read |
Each tool also accepts a 1:1 hyphenated protocol alias (discover-context for discover_context) — underscores replaced by hyphens, nothing more. The underscore names are canonical.
What is read-only, preciselyDirect link to What is read-only, precisely
"Everything is read-only" is not accurate, and Metatate does not advertise it that way. Here is the honest breakdown of the nine tools:
- Eight of nine advertise
readOnlyHint: truein their MCP registration — every tool exceptrequest_access. - Six are operationally pure reads.
discover_context,get_decision_context,inspect_data_meaning,inspect_governance_rules,explain_why, andcheck_requestread and return; nothing is persisted as a result of the call. authorize_useandvalidate_query_contextwrite durable evidence. Each answer is recorded as a citable decision-ledger record before the answer is returned. They advertisereadOnlyHint: truebecause they never change your governance — but they are not side-effect-free, and if the evidence write fails the call fails withinternal_errorrather than returning an uncited answer.request_accessis the sole workflow-state write and advertisesreadOnlyHint: false, withdestructiveHint: falseandidempotentHint: trueset explicitly (the MCP spec would otherwise defaultdestructiveHintto true for a non-read-only tool). It requires a separaterequesttoken scope, so a default token cannot reach it at all.
What holds across all nine, without exception:
- No tool mutates governance state. Policies, instructions, deployment publications, and served decision rows are never written by a tool call.
request_accesswrites a review request — a workflow row for a human steward — and nothing else. - No tool touches your data platforms. Metatate never enforces, masks, blocks, executes, or pushes anything into your systems. Answers are inputs to your agent's judgment; enforcement stays where it always was.
- No tool takes a decision on your behalf. Every decision-bearing answer carries
advisory: true.
What tools readDirect link to What tools read
Decision-bearing tools read exactly one source: the deployed instruction decisions of your workspace's current deployment publication. Draft policies, unpublished edits, and superseded publications are never consulted. inspect_data_meaning reads published data-meaning facts and is not a decision-bearing path.
Because answers come only from published state, they are typed. A tool returns answered, review_required, or not_enough_published_state — missing, conflicted, unresolved, or out-of-window serving state is reported as a typed answer state with a stable reason code, never as a fabricated decision. Zero applicable published rows is not_enough_published_state, by design. See Typed answer states for the full model, and the quick start to get a first publication live.
The in-app preview (the Tools tab of your workspace's MCP module) and the live endpoint share the same answer model, so what you try in the app is what agents get.
Who is asking, for what, and whenDirect link to Who is asking, for what, and when
authorize_use and validate_query_context answer three questions the decision function used to ignore. All three are derived server-side; none is a caller assertion.
Actor. The evaluated role is the token's own bound_role, read from the verified token row. Tool inputs are strict, so an invented bound_role, actor_role, or role key is invalid_parameters. Matching is trimmed, case-sensitive byte equality — Analyst and analyst are different roles, and there is no role hierarchy. A token with no bound role fails closed: role-addressed allows become require_review, while restrictive outcomes are untouched.
Purpose. Both tools accept an optional purpose_key from a global, versioned, closed registry. It is decision-bearing, but only where a usage_guidance rule participates in the answer. See Purpose keys for the complete semantics, including what purpose can and cannot do.
Time. A policy version may declare a validity window. Every tool that serves instruction rows applies the same half-open predicate effective_from <= evaluated_at < effective_until. When rows exist for the asset and scenario but none is in force, the answer is not_enough_published_state with reason not_currently_effective — never a silent absence and never an invented decision. discover_context is deliberately validity-blind: it reports published coverage, not current effectiveness.
Every authorize_use and validate_query_context answer, in all three states, states the context it was evaluated in:
| Field | Meaning |
|---|---|
evaluated_at | RFC 3339 UTC instant, minted once per call and identical to the ledger record's. |
evaluated_purpose | {purpose_key, purpose_category, registry_version}, or null when the call stated no purpose. |
evaluated_actor_role | The token's bound role, or null. |
get_decision_context and inspect_governance_rules carry evaluated_at (they filter validity windows by it) and serve rows un-substituted — inspection always shows the authored rules, not a purpose-adjusted view. Every served instruction row carries its own effective_from and effective_until.
on_behalf_of is an optional audit-only field on both tools: trimmed, single-line, 1–320 code points. It is decision-neutral by contract — no evaluator reads it, and a role name placed there changes nothing — and it is never echoed back in any answer.
Decision evidence you can cite laterDirect link to Decision evidence you can cite later
Every authorize_use and validate_query_context answer is recorded as a durable record before it is returned:
authorize_useanswers carryauthorization_id. This is not the same asdecision_id, which identifies a published serving row. The two have distinct explain surfaces.validate_query_contextanswers carryvalidation_id, plus afinding_idon every finding.
Pass any of them to explain_why, in this session or months later. Ledger records store only normalized, redacted evidence — no raw SQL and no raw use text ever appears in an explain output. Raw content is stored only in a protected admin/owner-readable table, and only when your workspace turns on the corresponding privacy switch (both default off).
Closing the loop: the request laneDirect link to Closing the loop: the request lane
A non-allow answer used to be a dead end for an agent. It no longer is:
authorize_usereturnsconditional,review_required, a deny, or an insufficient-state answer — with anauthorization_id.- The agent calls
request_accessciting thatauthorization_id, which writes a durable review request for a workspace steward. Confirm with your human first — see the note below. - A steward reviews it in the app and resolves it — granting a scoped exception, denying, or flagging that the policy itself needs changing.
- The agent polls
check_request. When an exception was granted, it gets theexception_id. - The agent retries
authorize_usewithsatisfied_conditions: [{kind: "approval", exception_id}]. The server verifies the exception in SQL — subject, liveness, and scope — before anything is satisfied.
Two things to design for. First, request_access requires the request token scope, which is granted only by deliberate issuance or rotation — a default token cannot reach the lane at all. Second, Metatate performs no server-side confirmation before a request is written: there is no elicitation round-trip and no two-phase commit. The honest readOnlyHint: false annotation is what a compliant MCP client uses to prompt its user before invoking a write tool, and that client-side prompt is the only place a human sees the request before it is filed. If you are building a custom client, implement it.
See Request access for the full contract and the worked example, and Review requests in the app for the steward's side.
Naming conventionsDirect link to Naming conventions
Every tool input and output key is snake_case (scenario_key, decision_id, can_proceed_now, publication.publication_id) — there are no camelCase aliases. The one documented exception is the contents of a served instruction's parameters object (and the nested payloads inside obligation projections): those are your authored policy DSL carried through verbatim.
MeteringDirect link to Metering
Every accepted tool call consumes one metered MCP call against your workspace's plan quota. Calls refused before input parsing — invalid_parameters and insufficient_scope — are not metered. When the quota is exhausted, calls fail with a typed quota_exceeded error rather than degrading silently. See Plans for quotas.