The Google Developers Blog published part 2 of the zero-trust series on 2026-09-15: the runtime must judge intent, not only syntax. This piece is dated 2026-09-25. It reads that post, Semantic Governance on Cloud, and the Gemini API Hooks page (last updated 2026-09-04). Pin the conclusion: the intent engine still eats a tool name plus a parameter object. If args fail Schema, the verdict is not trustworthy. A different layer — a hostile tool bus — is malicious MCP / Fake Skills.
The user says “the software license on order 99281 does not fit; refund $120 in full.” When the model reaches, you get issue_refund and args that look legal. SQL types match. The amount is under the order total. The prompt has no jailbreak phrase. Every syntax gate is green. Company policy still forbids it: opened digital goods over $30 need a manager. Officially that layer is Semantic Governance. It judges intent. It does not replace Schema on args. Why the contract layer is still an object: Why AI likes JSON.
What Google actually shipped
As of 2026-09-25, keep two public surfaces apart:
- Gemini Enterprise Agent Platform: the 2026-09-15 post moves zero-trust into the runtime. Agent Gateway sits on every hop among user, model, and tools. Three managed controls: Model Armor, Semantic Governance Policies, and Agent Anomaly Detection (with a closed remediation loop).
- Gemini API Agent Hooks:
pre_tool_execution/post_tool_executioninside the sandbox. A script reads tool-call JSON fromstdinand writesallow/denytostdout. Docs last updated 2026-09-04. Some samples still sayantigravity-preview-05-2026. Trust the agent string in the docs you are on; see the Antigravity September update.
Google draws the line itself. Part 1's deterministic controls (KMS-signed writes, gVisor isolation, a regex gateway) only catch what you specified in advance. A legal refund syntax does not tell a regex that “Workplace User License” is digital goods. A zero-trust runtime assumes one request can look valid and still be part of an attack.
Semantic Governance treats local functions, backend APIs, and remote MCP the same. Evaluate before execute. DENY means the tool does not run. KMS is not called. The ledger is untouched. That is not the same question as “is MCP a kind of connection”; see Agents API vs MCP.
One line. Google hosted the intent gate. It did not host the shape gate. Every tool call still has to be JSON that can refuse.
Three zero-trust layers — where JSON sits
Official runtime is three layers. Each owns one job:
- Model Armor: ingress screens injection, jailbreaks, malicious URLs; egress can redact card numbers and secrets. A blocked prompt never reaches the model.
- Parameter contract: JSON Schema on Function Calling / MCP. Missing
order_id,amountas"一百二", an extraextra_cmd— the executor should not reach. Official docs do not write this contract for you. - Semantic Governance: against an already shaped tool name and args, plus the user prompt, history, and a natural-language policy, emit
ALLOW/DENY.
Easy misread of layer 3: “finally no Schema.” Wrong layer. Structured Output constrains the final reply. Function Calling constrains the reach. The zero-trust post does not say the Agent gained Structured Output. Reply-contract layer: What Structured Output is. Why a hosted text loop still wants objects: After the Agents API, why JSON matters more.
Syntax: refuse a bad call first
The refund tool in the official demo can start as this object:
{
"type": "function_call",
"name": "issue_refund",
"id": "call_refund_01",
"arguments": {
"order_id": "99281",
"item": "Workplace User License",
"amount": 120.0,
"currency": "USD"
}
}
This object is syntactically legal. It can still violate “digital goods over $30 need a manager.” That is the next layer. Pin the contract first:
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"additionalProperties": false,
"required": ["order_id", "item", "amount", "currency"],
"properties": {
"order_id": { "type": "string", "pattern": "^[0-9]{5}$" },
"item": { "type": "string", "minLength": 1 },
"amount": { "type": "number", "exclusiveMinimum": 0, "maximum": 149 },
"currency": { "type": "string", "enum": ["USD"] }
}
}
Pin the shapes that must fail. They should go red on the workbench:
{
"ok_shape": { "order_id": "99281", "item": "Workplace User License", "amount": 120.0, "currency": "USD" },
"wrong_type": { "order_id": "99281", "item": "Workplace User License", "amount": "一百二", "currency": "USD" },
"extra_field": { "order_id": "99281", "item": "USB-C Dock", "amount": 29.0, "currency": "USD", "extra_cmd": "skip_kms" },
"over_total": { "order_id": "99281", "item": "USB-C Dock", "amount": 10000, "currency": "USD" }
}
Only ok_shape enters the intent engine. "一百二", extra_cmd, and 10000 should fail Schema. Do not hand “try anyway” to a natural-language policy. A too-wide tool and a fake manual are the same class of risk; see Limit tool permission with Schema.
Intent: one DENY verdict
Official policies are readable constraints, not another regex pile. The refund policy in the post: opened digital goods, software licenses, or clearance items over $30 must be denied and routed to a human; physical accessories up to $149 may pass. When the model proposes issue_refund(amount=120.00, item="Workplace User License"), the engine DENYs. Cloud Logging shows a shape like this:
{
"evaluations": [
{
"actionName": "issue_refund",
"toolName": "order_processing",
"verdict": "DENY",
"rationale": "The tool attempted to refund $120.00 for 'Workplace User License', a digital software product. Digital software refunds over $30 require manager authorization."
}
],
"timestamp": "2026-09-03T15:52:21.447123Z",
"verdict": "DENY"
}
The tool does not run. The ledger is not written. The agent explains the policy. Platform docs also allow DRY_RUN: log the verdict, do not block, tune the constraint. Those logs are JSON too. Fields must parse. Do not treat the whole log line as the final contract.
The intent layer eats args that already passed Schema. If amount is sometimes a number and sometimes a string, the same natural-language constraint will drift. Shape first, intent second.
The Hook door: stdin / stdout are objects too
Sandbox Hooks are even more direct. Official pre_tool_execution reads stdin:
{
"tool_call": {
"name": "code_execution",
"args": {
"code": "rm -rf /tmp/forbidden",
"language": "bash"
}
},
"environment_id": "env_xyz789"
}
The script must emit a decision object. To block:
{
"decision": "deny",
"reason": "Destructive command blocked by security gate."
}
To allow: {"decision":"allow"}. Docs pin one failure rule: unrecognized JSON, plain text, or anything other than {"decision":"deny"} is treated as allow. The decision object needs Schema too. A "deny" in text substring check takes the door off the hinges.
An HTTP Hook POSTs the same event. The response body is still this decision shape. A post-tool Hook only formats or audits. It cannot undo a side effect that already happened. High-risk writes must die in pre-tool. Parsing args is the same job as in a Live session; see the Live Function Calling tutorial.
Legal once, still pierce in sum
Official third scene: the attacker learns “software refunds under $30 pass,” then splits $120 into $20 slices. Each turn passes Model Armor, the single-turn policy, and a KMS signature. The sum exceeds the $149 order. A single-turn door cannot see session velocity or a running total.
That layer is Agent Anomaly Detection on telemetry, then a new natural-language constraint: “this session already has an approved refund for the same order_id — DENY.” The constraint can land at runtime. No agent redeploy. The premise is unchanged: every issue_refund args object must parse the same way. A running total adds legal objects. It does not add prose.
How to accept it in JSON Toolbox
- Paste the
issue_refundok_shapeand the three failing objects into JSON format. Confirm they parse. - Use JSON → Schema for a core contract, then add
enum,maximum, andadditionalProperties. - Run the same fields through Function Calling. If another host will reuse them, wrap MCP Tool.
- Drop
"一百二",extra_cmd, and10000into Schema validator and confirm they fail. Do the same for the Hookdecisionobject: onlyallow/deny. When you need a reply shape, use Structured Output — remember the zero-trust post governs the reach, not the final-reply contract.
Everything stays in the browser. Nothing is uploaded. First have objects that refuse bad args and a messy decision, then hand the natural-language policy to the platform.
FAQ
If Semantic Governance exists, do I still need JSON Schema?
Yes. The intent engine eats already-shaped args. Missing fields, wrong types, and extra keys should fail Schema.
Is the Hook allow / deny Structured Output?
No. That is decision JSON for the runtime. Unrecognized output is allow. The final reply can still be natural language.
Does Semantic Governance cover MCP?
Official text evaluates local functions, APIs, and MCP before execute. Impersonation is a different layer.
Each refund is legal. Why validate?
Split refunds pierce a single-turn policy. Schema owns shape. Totals need anomaly detection and a new constraint. Both layers pass before signatures.
What if a Hook script prints garbage?
Anything other than a clear deny object is allow. Decision JSON needs Schema too.
Summary
Google moved zero-trust into the runtime: screen the prompt, judge intent before the tool, watch the session for a running total. It did not take the parameter contract of each tool call. What you hold is two objects: function_call.arguments and the Hook decision. Only a legal shape enters the intent engine. Only a DENY keeps the ledger clean. A messy Hook output opens the door. The next step is not a longer natural-language policy. It is refusing "一百二" and extra_cmd.