Control what tools can do
How an agent uses tools, and how you decide which tools each run may use. A tool call you did not allow is refused before it happens.
An agent on its own can only produce text. A tool is how it reaches anything outside itself — reading a file, searching a collection of documents, calling a service you have connected. Kortecx treats every one of those reaches as an action that has to be authorized, not as something the model gets to decide on its own.
Two words do a lot of work on this page, and they do not mean the same thing:
- Registered — Kortecx knows this tool exists. It is in the inventory, with a name, a version, a description, and a declared set of things it needs (a network host, a folder, a credential).
- Fireable — this run is actually allowed to call it, right now, with these arguments.
Registering a tool grants no authority. A ranking score grants no authority. The only thing that authorizes a call is what you granted that run — the exact tools, the folders it may touch, the addresses it may reach and the credentials it may use — and Kortecx re-checks that grant at every single call.
The inventory
The registry is the standing list of what tools exist. It is kept separately from your run history, so registering or removing a tool never changes what a past run did.
Each row shows the tool's name and version, its kind, whether it is a built-in, its network scope, and its description — the governance view of what exists, not what is allowed.
The built-ins
On the managed cloud your inventory opens with fs-read@1 and fs-write@1 listed but never grantable to a run, beside a few of the platform's own built-in tools, which a run can fire. None of them can be removed. A self-run runtime ships these three, and they cannot be removed there either:
| Tool | What it does |
|---|---|
fs-read@1 | Reads bytes from a path the run's folder grant already covers. Read-only. |
fs-write@1 | Writes the complete intended file content to a path the grant covers. Overwrite-only — no append, no partial writes. |
text-summarize@1 | A deterministic text-summarizing transformation. No network, no filesystem. |
A deregistration attempt against any of them is refused — the command prints not removed (absent or a built-in).
A built-in in the inventory is still not fireable
fs-read@1 being present says only that the runtime knows how to read a file. It reads nothing until a run's grant both names that exact tool and covers the exact folder the arguments point at.
The authority gate
When a model proposes a tool call, the proposal is untrusted text. The runtime decodes it and then walks an ordered set of checks. Every one has to say yes. The first no stops the action.
The first two are checked as soon as the proposal is read, before anything is dispatched. If a turn proposes several calls, all of them have to pass — one bad call rejects the whole turn rather than firing the rest.
Is this exact tool resolvable and granted? The check is an exact match on the (name, version) pair. Not a prefix, not a fuzzy match, not "close enough". web-search@2 is not web-search@1. The refusal reads the proposed tool ... is not granted to this run or is no longer registered.
Are the arguments the right shape? The proposed arguments are validated against the tool's declared input schema — the right keys, the right types — before anything runs. The argument bytes are carried through verbatim and checked for shape; they are never executed or interpreted along the way.
The remaining checks happen where the call is actually dispatched, and they run in this order:
Is this tool declared for this step at all? The step names the tools it can use. A name that is not in that list is unknown, and the call ends here.
Is the tool being used the way it declared it can be used? Each tool declares the shapes of call it supports. A request for one it does not support is refused.
Is this exact tool inside what this run was granted? The (name, version) pair is re-checked here against the run's grant. The earlier check does not stand in for this one.
Are the address, the folder, and the credential inside what the grant allows? Three subset checks, in that order. The network host the call wants must sit inside the granted network scope. The path must sit inside the granted folder scope. The secrets the call needs — both the ones it declares and the ones the tool itself is configured with — must sit inside the granted secret scope. Any one of the three outside the grant and the call is refused.
Only after all of that does the tool actually run.
A refusal is still a fact
A refused call does not silently vanish, and it does not kill the run. The turn is recorded as rejected with a reason, and that record stays — you can read it back. Reading turns is limited to the platform's own engineers today; a run's summary counts, for each tool step, how many turns were refused and why.
Each turn shows how it settled — pending, answer, tool, rejected, or dead_lettered — and, for a rejection, why. The model is then re-prompted over the reason and gets to try again, bounded by the run's turn and tool-call budgets. Common reasons and what they mean:
| Reason | What to do |
|---|---|
the proposed tool ... is not granted to this run or is no longer registered | Grant that tool to the workflow or agent, or expect the model to pick one it does have. |
the arguments for ... do not match its inputSchema | The keys or types are wrong. Check the tool's declared schema. |
dead_lettered (no reason is shown) | The run used up its turn or tool-call budget without a usable answer. Simplify the task. |
The granted-tool menu
For a model to propose a sensible tool call, it has to know what it has. On a tool-eligible turn the runtime prepends a short menu to the prompt, built from what that run was granted:
You can call the following tools:
name: slack/search
version: 1
Search messages in the connected workspace.
Inputs:
- query (str, required)
- limit (integer, optional)
Example: {"query": "budget"}Each entry leads with the exact callable name and the pinned version, because the runtime matches the version exactly. If the registry cannot resolve a granted tool, the entry degrades to its name, version and the call envelope shape rather than being dropped.
The menu lists only the tools granted to that run — never the whole inventory. A tool this job is not allowed to use never appears in the prompt at all, so a prompt-injection attempt has nothing to name.
The menu is advisory. It shapes what the model is likely to ask for; it authorizes nothing. The checks above still run on every call.
The folder the file tools can reach
The managed cloud grants no folder: there, fs-read@1 and fs-write@1 are listed and can never be granted to a run. On a self-run runtime, the host-side file tools are off unless two things hold: the runtime has been given a folder they may read, and a model is being served — with no model there is no agent loop to drive them. When both hold, the runtime registers the read-only file tools and seeds a starter workflow whose grant names them plus a folder scope of exactly that directory.
Paths are canonicalized, so an argument cannot walk out of the granted folder. Outside that scope, the folder check in the gate refuses the call.
In the console
You can see these in the console. What the console does show is the part you most often need after the fact: what a run reached for, in order, which calls came back, and which were refused with the reason. See seeing what happened.