What is _the_ proper way to intercept tool calls modify them or dynamically approve/reject them?

What is the proper way to intercept tool calls modify them or dynamically approve/reject them?

Answered by divyaravi11992 in 892929022

The pattern that works today is wrapping your tools rather than intercepting at the framework level.

Instead of looking for a framework-level interception hook, implement your Tool conformance so that the tool's call method is itself the approval gate. Inside it, before performing the actual work, run your validation/approval logic — check the arguments, apply your own repair if they're malformed, and either proceed, throw, or return a result that asks for confirmation.

For dynamic approve/reject specifically: have the tool return a result indicating "needs confirmation" rather than executing, then surface that to the user, and on approval invoke the actual operation in a follow-up turn. This keeps the model from autonomously executing high-impact actions — the tool acknowledges but defers the real work behind your confirmation surface.

For argument validation/repair before execution: don't trust the model's tool arguments blindly. Validate against your own constraints inside the tool, and if they're off, you can either throw (letting the model retry) or coerce to the nearest valid value. This is essentially defensive programming at the tool boundary.

There isn't a first-class "intercept all tool calls" middleware in the framework that was surfaced at the labs — the tool boundary itself is currently the right place to put this logic. If you need it centralized across many tools, a small protocol wrapper that all your tools adopt gives you one place for the approval/validation logic.

— Divya Ravi, Senior iOS Engineer

Accepted Answer

The pattern that works today is wrapping your tools rather than intercepting at the framework level.

Instead of looking for a framework-level interception hook, implement your Tool conformance so that the tool's call method is itself the approval gate. Inside it, before performing the actual work, run your validation/approval logic — check the arguments, apply your own repair if they're malformed, and either proceed, throw, or return a result that asks for confirmation.

For dynamic approve/reject specifically: have the tool return a result indicating "needs confirmation" rather than executing, then surface that to the user, and on approval invoke the actual operation in a follow-up turn. This keeps the model from autonomously executing high-impact actions — the tool acknowledges but defers the real work behind your confirmation surface.

For argument validation/repair before execution: don't trust the model's tool arguments blindly. Validate against your own constraints inside the tool, and if they're off, you can either throw (letting the model retry) or coerce to the nearest valid value. This is essentially defensive programming at the tool boundary.

There isn't a first-class "intercept all tool calls" middleware in the framework that was surfaced at the labs — the tool boundary itself is currently the right place to put this logic. If you need it centralized across many tools, a small protocol wrapper that all your tools adopt gives you one place for the approval/validation logic.

— Divya Ravi, Senior iOS Engineer

In addition to implementing Tool conformance, the Foundation Models framework also introduced a new DynamicProfile API that allows you to add event listeners like onToolCall.


For tool approval and rejection, the session Secure your app: mitigate risks to agentic features - WWDC26 has some nice examples.

For dynamically modifying tools, there's a detailed session: Build agentic app experiences with the Foundation Models framework.

Cannot change the accepted answer once marked 🤦‍♂️… so, while it should be obvious, noting manually that official answer should definitely be the one!

@Apple Designer, given onToolCall's behavior to propagate any error thrown to the respond/response, one can't utilize it for stopping individual tool calls without ending the current turn's loop entirely.

I think that to achieve a fine-grained behavior one still needs to resort to Tool conformance (wrapping or extending existing Tools) to have the tool respond with the non-fatal failure/feedback for the model to continue, right?

I may still be missing a handy alternative to do this, and there can be reasons to not want to give it first class support, but filed a suggestion (FB23092325) just in case it is useful.

What is _the_ proper way to intercept tool calls modify them or dynamically approve/reject them?
 
 
Q