A model can produce a perfectly shaped tool call that the current user is not allowed to execute. Structured output helps the application understand the request; it does not grant authority to perform it.
I design a tool-using agent as a planner operating inside an application-controlled execution boundary. The model proposes the next action. Server code decides whether the action is permitted and how its result can be returned.
Make the tool contract describe risk and scope
A tool definition needs more than a name and JSON schema. I want to know whether it reads data or changes it, which permissions it requires, how tenant scope is established, and whether the operation needs approval.
Argument validation should cover both structure and meaning. A valid string can still identify another tenant’s record. A well-formed URL can still point to an internal network resource. A numeric quantity can be outside the allowed business range.
I would initially expose a small set of useful read-only tools. This reduces the number of execution behaviors that need to be proven before introducing write operations.
Authorize again when the action executes
The application should resolve the user’s current permissions and the target resource’s ownership at the point of execution. A model-supplied tenant ID or role should never be accepted as authority.
This matters in multi-step runs. Membership may change, a resource may move, or a queued action may execute after its original context has expired. Authorization should not be a one-time property of the conversation’s first message.
The same principle applies to approval. Approval should be tied to a specific action and its relevant arguments. If the agent changes the target or amount afterward, the earlier approval no longer describes what will happen.
Keep external content in its own trust category
A retrieved page, customer message, or tool result may contain instructions addressed to the model. Those instructions do not acquire authority simply because they appear in the context window.
OWASP’s prompt injection guidance discusses the risk of untrusted content influencing model behavior. My execution boundary assumes the planner can be confused and still prevents it from crossing permissions that server code enforces.
Tool outputs should also be minimized. If the task needs an order status, returning every customer field creates unnecessary exposure. Sensitive fields can be omitted or redacted before the result reaches the model.
Bound the loop, not just each call
A useful agent needs limits on tool iterations, elapsed time, total cost, and repeated failures. Per-call limits are insufficient if the planner can start another call indefinitely.
I distinguish productive progress from repeated attempts with equivalent arguments. If a lookup returns no result twice, the next useful step may be a clarifying question rather than a third variation of the same search.
A run should end in a recognizable state: answered, needs clarification, awaiting approval, handed to a person, or failed with a recoverable reason. That makes both the interface and the operator’s job clearer.
Record an action trail a human can inspect
The audit record should show the proposed tool, validated arguments, authorization decision, relevant approval, result status, and timing. It does not need to expose private model reasoning to explain the application’s decisions.
For external writes, I add a stable operation ID and idempotency strategy. Retrying the planning step must not repeat a completed business action.
The first adversarial tests I run ask an ordinary user to retrieve another tenant’s data, place instructions inside a tool result, alter arguments after approval, and repeat a completed action. The agent earns more autonomy only when these boundaries remain understandable and enforceable.
Updated 25 September 2026.
