An agent should know its blast radius up front
A credential does not announce its own scope. The model holding one will assume a scope anyway.
Here is the first response from a session that opened this morning. It is the reply to an identity call, the equivalent of whoami, made before any other work:
Four of those fields are ordinary. The three highlighted ones are the interesting part, because they are the only place in the entire session where the reach of that credential is written down. Nothing in the shape of an API key says whether it addresses one tenant or several hundred. Nothing in the shape of a tool call says so either. If the identity response does not state it, the number does not exist anywhere the model can reach.
The scope a model assumes by default
Absent a statement, a model infers its scope from whatever is in front of it. That connection stamps domain: "ogpss" as its default, so the natural working assumption is that the session operates on ogpss. The assumption is wrong, and the platform says so in the same payload:
This is a field that looks like a fence and holds nothing. It is the value the connection volunteers about itself, and it constrains no operation whatsoever. A model reading it has every reason to treat it as a limit and no way to discover that it is a default.
The gap between those two readings is one parameter. An agent that believes it is confined to one tenant, and is in fact able to address sixty-seven of them, is not misbehaving when it writes to the wrong one. It is acting correctly on the only information it was given.
Fields that look like discriminators and are not
The same problem appears one level up, at the question of which environment you are connected to. The identity response answers it directly:
A URL is an attractive thing to reason from. It is always present, it is stable, and it looks like an address. It is also identical across every environment, which makes it the most available field in the response and the least informative one. That combination is what produces a confident wrong answer rather than an obvious failure, and a confident wrong answer about which environment you are in is expensive.
Documenting which of your own fields a caller must not reason from costs one line, and it closes an inference the model would otherwise make silently.
Declare the refusals before they happen
The same call, answered for a second credential on the same platform, names an operation that will refuse:
The refusal exists either way. The question is when the agent learns about it. Discovered by hitting it, a refusal costs a round trip, a failed tool call in the transcript, and a model that now has to work out whether the operation was partially applied. Declared at the moment the agent learns who it is, the same refusal costs one line and shapes every plan the model makes afterwards.
The two halves belong together. A good error payload repairs a call that has already failed. An identity payload prevents the class of call that would fail. Neither substitutes for the other.
Why the layer above cannot supply this
An agent framework can add a line to the system prompt: you are operating on tenant X. That line describes the scope the author intended. Whether it is the scope that holds is a fact about the authorisation layer on the far side of the API, and the framework has no read on it. The two copies drift, the prompt is the copy the model trusts, and prompt-level constraints are advisory in exactly the way prompt-level guardrails are.
The distinction is sharper than it sounds. Consider a read where the caller supplies a tenant filter that contradicts the one it is allowed to use:
The requested filter named one tenant. The executed filter named another. The server did not reject the request, it rewrote it, and then reported the rewrite in the response so the caller could see the difference between what it asked for and what ran. That reporting is only possible from the position that performs the rewrite. A wrapper never holds the executed query; it holds the one it sent, which in this case is the wrong one. The same mechanism that scopes every query before it runs is what makes the echo truthful.
So a framework can report intent and observe outcomes. Enumerating authority before the first call requires standing where the authority is enforced.
What to return from your own identity call
Four things, in rough order of value:
- The quantity, not the category. A role label such as
admintells a model that it has authority. A count tells it how much. Those drive different plans. - Every field that resembles a boundary without being one. If a value is stamped on each response as a default, say that it is a default. Repetition reads as constraint.
- Every field that resembles a discriminator without being one. Anything stable, always present and identical across environments will get used to tell environments apart unless the payload says otherwise.
- The operations that will refuse, and what unlocks them. Naming the confirmation flag at identity time is the difference between a plan and a retry loop.
None of this requires new infrastructure. The reach, the defaults, the cluster label and the confirmation flags are all resolved at the moment the session opens, which is the moment the identity call is answered. This matters more under a stateless contract, where every call carries its own scope rather than inheriting one, because the identity response is then the only place the whole picture appears at once.
If you own an identity endpoint and a model might call it, those four fields are an afternoon in the handler, and they get read at the start of every session for as long as the API exists.