PATTERN Cited by 1 source
Policy-as-UC-function attached to MCP¶
Pattern¶
Express agent-tool-call admission control as catalog-managed code (a UC function) that is attached to the registered MCP server, not the calling agent. The function is evaluated before every tool call and returns a ternary verdict (allow / deny / consent). Deny is fail-closed.
The pattern's load-bearing properties:
- Policy is code in the catalog. The policy is a function — versioned, owned, audited, governable like any other UC asset — not a separate control-plane configuration.
- Policy is attached to the resource (MCP server), not the agent. Every agent that calls the MCP gets the same policy, regardless of framework. (Governance travels with resources.)
- Decision is ternary.
consentis first-class — the policy can require human-in-the-loop confirmation rather than just blocking. - Decision inputs are tool name + args + caller identity. The runtime call signature, not just static permission.
- Fail-closed on deny. Security-side default.
Canonical statement on the wiki¶
"We built Service Policies, which are UC functions, managed in UC and attached to registered MCPs in Unity Catalog that control which tool calls succeed. Every tool call is evaluated before execution: based on the tool name, its arguments, or the identity of the caller, the policy returns allow, deny or asks for user consent. If the policy evaluation results in a 'Deny', the call is blocked." — Source: sources/2026-05-20-databricks-governing-ai-agents-at-scale-with-unity-catalog
Why a UC function (not a separate policy DSL)¶
The structural payoff: the policy inherits UC's existing primitives. UC already has:
- Versioning + audit lifecycle for code assets.
- Permission model for who can read / modify policies.
- Evaluation engine for ABAC row-filters, column-masks, governed tags.
- SQL + REST + UI surfaces for managing assets.
By making the Service Policy "a UC function", Databricks reuses every one of these properties without building a parallel control plane. The same engine that evaluates "can user X see column Y?" now evaluates "should agent Z be allowed to call tool T with args A?".
Why attach to the MCP server (not the agent)¶
Two reasons:
- Framework-agnostic. An agent built on LangGraph and one built on CrewAI both invoke the same MCP server; the policy applies to both without per-framework configuration. The policy travels with the resource, not the caller. (concepts/governance-travels-with-resources.)
- Centralised review. A security team reviews the policy on the GitHub MCP server once and the review applies to every agent that calls it. Per-agent policies require N reviews; per-resource policies require 1.
Why the verdict is ternary¶
allow and deny are familiar from access control. consent is the addition Databricks names explicitly:
"the policy returns allow, deny or asks for user consent." — Source.
The architectural value: many runtime decisions are too risky to allow blindly but too disruptive to deny outright. Asks for user consent means the system can pause, surface the proposed action to the user, and proceed only on confirmation. This is the runtime analog of destructive-operation confirmation but expressed through the policy mechanism rather than as ad-hoc client-side prompts.
Architectural shape¶
agent → tool call (name, args)
│
▼
Unity AI Gateway
│
│ pre-execution evaluation
▼
Unity Catalog
│
│ Service Policy (UC function bound to this MCP)
│ evaluate(tool_name, args, caller_identity)
│ → 'allow' | 'deny' | 'consent'
▼
├─ allow → forward to MCP, audit-log
├─ deny → block, audit-log, return error to agent
└─ consent → pause; surface to user; on confirmation, re-evaluate
Decision-input matrix¶
| Input | Available to policy | What it lets the policy reason about |
|---|---|---|
| Tool name | ✓ | Coarse: which capability is being invoked |
| Tool arguments | ✓ | Fine: "delete this specific repo" vs "list repos" |
| Caller identity | ✓ | Per-user / per-team / per-environment differentiation |
| Request body of LLM call | (not disclosed) | Would let policy reason about why the agent chose this tool |
| Other UC table data | (likely, since it's a UC function) | E.g. lookup against an allow-list table |
Sibling shapes¶
- patterns/dynamic-content-filtering-in-mcp-pipeline (Redpanda Connect, 2025-04-03) — MCP-boundary policy expressed as Bloblang / Starlark / Python code for full programming-language flexibility. Same programmable policy at MCP boundary shape, different DSL substrate (programming languages vs UC functions).
- patterns/per-tool-authorization-decorator (Pinterest, 2026-03-19) — per-tool authorization expressed as a decorator on the tool implementation itself (within the MCP server). Different attachment point: decorator-on-tool-code vs UC-function-attached-to-MCP. The Databricks shape sits outside the MCP server (catalog-mediated); the Pinterest shape sits inside (decorator on the tool function).
- patterns/runtime-governance-enforcement-layer (LangGuard, Databricks 2026-04-27) — runtime-policy-enforcement for agentic workflows. Different surface: LangGuard intercepts agent-workflow actions; Service Policies intercept MCP tool calls. Both are runtime synchronous gates.
When to use this pattern¶
- The MCP-boundary is the load-bearing tool surface and you want centralised, policy-as-code admission control.
- You already operate a catalog (Unity Catalog or equivalent) and want policy to inherit its lifecycle (versioning / audit / ownership).
- You need ternary verdicts (allow / deny / consent), not just boolean.
- You need runtime-context-aware policy (tool args, caller identity, time-of-day) — not just static permission grants.
When this pattern is overkill¶
- Single-tool / single-agent deployments.
- Environments without a catalog substrate to host the policy function.
- Static-permission-only environments where deny / consent decisions are made at IAM time, not per-call.
When this pattern alone is insufficient¶
This pattern covers layer 2 of three in the patterns/three-layer-agent-control composition. It does not cover:
- Layer 1 (permissions) — who can reach this tool at all. Must be answered by OBO or equivalent.
- Layer 3 (content guardrails) — what content flows in / out. Must be answered by an inline content guardrail.
Seen in¶
- sources/2026-05-20-databricks-governing-ai-agents-at-scale-with-unity-catalog — canonical first wiki disclosure (2026-05-20). Concrete instance: systems/uc-service-policies.
Source¶
- Originating post: https://www.databricks.com/blog/governing-ai-agents-scale-unity-catalog
- Linked deeper-dive (not ingested): https://www.databricks.com/blog/stop-rogue-ai-how-unity-catalog-secures-your-agent-actions
Related¶
- systems/uc-service-policies — canonical concrete instance.
- systems/model-context-protocol — the tool surface this pattern attaches to.
- systems/unity-catalog — the policy substrate.
- concepts/runtime-policy-enforcement — parent concept.
- concepts/governance-travels-with-resources — sibling principle this pattern instantiates.
- patterns/three-layer-agent-control — the composition this is layer 2 of.
- patterns/dynamic-content-filtering-in-mcp-pipeline — sibling pattern with different DSL substrate.
- patterns/per-tool-authorization-decorator — sibling pattern with different attachment point.