A chat box lands in the corner of the operations screen and it can answer anything. That is the problem. The person using it has to work out what to ask, which of the four systems behind that screen owns the answer, and whether the reply is safe to act on. The application already knows all three things. The chat box has been handed none of them.
Set that against a workflow the application already runs. Releasing a credit hold has a fixed shape: read the account, list the open invoices, read the hold reason, clear the hold, write the audit row. A task-specific agent built for that workflow is handed those five capabilities and no others. Nobody can ask it about payroll, because nothing it holds reaches payroll. It is finished when the hold flag is clear and the audit row exists, which is a state a support person can read off the account without opening a transcript.
The Tool List Is the Scope
Scope usually gets written into the system prompt, where it reads like a rule: only answer questions about orders. The model treats that as guidance. What the agent can actually reach is the Tools collection on the chat options it was handed. Microsoft describes the loop plainly on the FunctionInvokingChatClient reference: when the client receives a function call in a response from its inner client, it invokes the matching AIFunction defined in Tools, produces a result, and sends that back, and the loop repeats until there are no more function calls to make or another stop condition is met. Nothing in that description consults the prompt.
So the scoping decision is a list. It belongs in the code that builds the client, and the paragraph of English in the prompt is not where it takes effect. Write down the workflow, then the smallest set of operations that completes it, then hand over exactly that set. The description attached to each tool when it is created is what the model selects on, which makes a vague description a functional defect: the function calling quickstart passes the name and the description as arguments so the model can tell the purpose of the function. Two tools described as get order information and fetch order details will be chosen between more or less at random, and the one that opens a database transaction is as likely to win as the one that only reads.
What Does Support See When a Tool Call Fails?
A tool that reads an invoice throws because the account number does not exist. The natural assumption is that this ends the interaction: the exception surfaces, the user sees an error, somebody files a ticket. That is not the default behaviour. When a function invocation fails with an exception, FunctionInvokingChatClient carries on making requests to the inner client, optionally supplying the exception information, so that the model can recover by trying other function parameters that might succeed. The agent tries again with different arguments, and the person watching the screen sees a pause.
Three published defaults decide what happens next. MaximumConsecutiveErrorsPerRequest is 3, so the loop tolerates three consecutive failing attempts and then rethrows the exception to the caller. MaximumIterationsPerRequest is 40, and the reference is explicit that the value must be at least one because it includes the initial request, so 40 is the whole round-trip budget for one question rather than 40 tool calls on top of it. IncludeDetailedErrors is false, which means the chat history receives a generic error message, not the full exception message.
Read those three together and the support question answers itself. The transcript handed to a support person contains a generic failure, three times over, and no account number anywhere in it. The real exception has not been thrown away: with detailed errors off, the raw Exception object stays available to application code on the Exception property of the function result. Log it there, with the tool name and the arguments the model chose, before the loop moves on. That log is what turns the agent failed into the invoice lookup ran three times against an account number that has never existed.
Switching IncludeDetailedErrors on is a real option and the reference says why: it can help the model bypass the problem on its own, for example by retrying the call with different arguments. The same page says what it costs, which is that raw exception information may be disclosed to external users. For an internal agent behind staff authentication that trade is usually worth making; for anything a customer talks to it usually is not. Either way it is an application decision with a security consequence, and the failure path is the part of an agent that gets designed last and read first.
The Identity the Tool Runs Under
A tool is ordinary application code, and the question of what it runs as tends to get settled by whoever writes the first one. The mechanism is documented: AIFunctionArguments carries the IServiceProvider that the FunctionInvokingChatClient was itself given, so a client constructed through standard dependency injection passes that provider all the way into the function, where it can be queried for anything the container holds.
That is the point at which two versions of the same tool diverge. One resolves the invoice service the screens already call, inherits the permission check that service performs, and returns what this user is entitled to see. The other opens its own connection with the integration account and returns whatever that account can read. Both look identical in a demo. The second answers questions about accounts the user was never allowed to open, in fluent prose, leaving no failed authorization for anyone to find afterwards.
State the rule before the first tool is written: a tool calls the same service a screen calls rather than opening its own path to the data. One permission model stays in one place, and it keeps working when the rules change. Where the underlying data is stale or scattered across copies, the tool inherits that too, which is a separate and earlier problem: fixing the retrieval layer comes first.
Definition of Done
Give the agent a finish line the application can check. A satisfied user is not one, and neither is a well-formed answer. The finish line is a row that exists, a status that changed, or a document that was filed. The credit hold agent is done when the hold flag is clear and an audit row names the agent as the actor.
That same definition is the only honest measure of whether the thing works. Conversation volume rises when the agent is confusing. Time spent in the box rises when it is failing. Holds cleared without a person opening the account is a number that moves in one direction, and it reads straight off the audit table the auditors already use. Start counting it in the first week, because the baseline worth having is the one from before anybody adjusted the prompt.
Where IKRC Fits
The model call is the small part of this. What takes the time is naming the five operations a workflow actually needs, deciding which service each tool resolves, and writing the failure so the support desk can read it without a transcript. IKRC does that work inside applications that are already running, through practical AI.
A useful first conversation is narrow. Name one workflow somebody completes by hand today, list the systems it touches, and say who picks up the phone when it goes wrong. Call IKRC at 646-783-1441 or email info@ikrc.co.
Related Reading
For the layer underneath all of this, read Before You Add AI, Fix the Data Retrieval Layer. For exposing a system to outside agents on purpose, read Customer-Facing MCP Servers.
Need this solved in your software?
IKRC builds the custom systems, integrations, and modernization work discussed in this article.