Permission
Coming soonpermission_mode gates every tool call the model makes. Set it on the Agent:
from mistralai.agents import agents
agent = agents.Agent(
model="mistral-medium-3-5",
instructions="Use the tools.",
permission_mode="deny",
harness=agents.Harness(tools=[deploy_service]),
)| Mode | What happens on a tool call |
|---|---|
"auto" (default) | The call runs. There is no gate. |
"deny" | The call is refused. The model receives permission denied: <tool> |
"ask" | The session opens an approval callback. The turn waits for your answer. |
The gate is unscoped. It applies to every tool call: your own tools and the sandbox built-ins such as bash and run_typescript. There are no per-tool rules.
Answer an "ask"
"ask" needs a session. The turn stops at an ApprovalCallback and continues when you answer it:
from mistralai.vibe.harness.app_server import session as harness_session
from mistralai.agents import agents
agent = agents.Agent(
model="mistral-medium-3-5",
instructions="Use the tools.",
permission_mode="ask",
harness=agents.Harness(tools=[deploy_service]),
)
async with await agent.create_session() as session:
prompt = [harness_session.TextContentBlock(text="Deploy the service.")]
async for event in session.run_turn(prompt):
if isinstance(event.payload, harness_session.CallbackOpenedEvent):
await session.answer_callback(
harness_session.CallbackResult(callback_id=event.payload.callback.id, output=True)
)The callback carries title, tool_call_id and the tool input, so you can show the user what the agent asks for.
output=True allows the call. {"allow": True} and {"decision": "allow"} do the same. Every other value refuses it.
:::caution agent.run() cannot answer a callback
agent.run() has no callback channel. With permission_mode="ask" the turn stops at the approval and does not terminate. Use create_session().
:::
create_session(permission_mode=...) sets the mode for one session. A deployment-backed session refuses the argument: it uses the mode pinned in its deployed Agent.
:::note A refusal is absent from the history
"deny", and an "ask" that you refuse, both compile to a pre_tool_call Hook skip. A skip writes no tool_call entry and no tool_result entry, so list_history() does not show that a call was refused. Record the refusal yourself if you need the trail.
:::
See also
- Custom hooks - permission is a
pre_tool_callHook. Write your own for a rule the SDK does not ship. - Tools - what the gate applies to.
- Sessions - the callback channel that
"ask"needs.