response.choices[0].message.tool_calls as a list of tool-call objects. Certior accepts that list in its native shape via verify_tool_calls() - no SDK migration, no proxy, no schema rewrite.
The full loop
What verify_tool_calls returns
For every input tool call, you get a dict augmented with:
| Key | Type | Meaning |
|---|---|---|
id | str | The call’s id (preserved from OpenAI’s shape). |
name | str | Tool name. |
input | dict | Normalized arguments. |
allowed | bool | Z3 verdict. |
reason | str | Block reason (empty when allowed). |
certificate | VerifiedCertificate | None | Signed cert on allow, None on block. |
redacted_input | dict | Arguments with PII redacted (when the policy redacts). |
pii_found | list[tuple] | Detected PII spans. |
verify_result | VerifyResult | The full result object. |
Per-call overrides
A call may carry its ownrequired_capabilities / cost_cents keys that override the tool_specs map. Use this when one tool’s permission profile depends on its arguments.
Anthropic and MCP
The sameverify_tool_calls() accepts Anthropic’s tool_use block list and MCP tool calls in their native shapes. The normalizer in certior.adapters.tool_use._normalize_tool_call handles each.
See also
- Custom loop -
Guard.verify()directly, no framework. - Certificates - what a successful verify produces.