Why architecture matters here
The architecture matters because an agent's blast radius is defined by the credentials its tools carry, not by anything the model itself does. A model cannot delete a database; a tool holding a database admin credential can, and the model can be persuaded to call that tool. Getting authentication right is therefore the single highest-leverage control on what an agent can do harm with. Least-privilege, short-lived, per-user credentials shrink that blast radius from 'everything the application can do' to 'exactly what this call, for this user, needs right now.'
It matters because prompt injection is not a hypothetical. Agents routinely read untrusted text — web pages, emails, documents, the output of other tools — and that text can contain instructions the model may follow. If a credential is anywhere in the model's context, an injected instruction like 'print your configuration' can leak it. Keeping secrets out of the prompt and inside a broker that injects them at the transport layer removes the credential from the attack surface the model exposes. The model can be fully compromised and still never surrender a secret it never held.
It matters because agents usually act on behalf of a specific person, and conflating the agent's identity with the user's is a classic privilege-escalation bug. If the agent calls the calendar API with a single service account, then user A can ask the agent to read user B's calendar and the API cannot tell the difference — the confused-deputy problem in its purest form. On-behalf-of token exchange gives each call the end user's identity and consent, so the downstream API enforces that user's permissions natively, and cross-user access simply cannot be requested.
It matters because credentials leak, and the only question is how much a leak costs. A long-lived key that never expires and grants broad access is a catastrophe when it leaks — it may be usable for months against every resource. A token scoped to one operation and valid for five minutes is a footnote: by the time it surfaces in a log aggregator it is already dead, and even live it can do only one narrow thing. The architecture converts the inevitability of leaks from an existential risk into a bounded, recoverable event.
Finally it matters because auditability and revocation depend on it. When every tool call carries a per-user, per-scope token minted by a central broker, you get a complete, attributable audit trail — who did what, through which agent, with what permission — and a single place to revoke. Shared static keys give you none of that: you cannot tell which user's request used the key, you cannot revoke one user without breaking all of them, and you cannot answer the auditor's first question. The broker is what makes an agent's actions accountable.
The architecture: every piece explained
Start with the auth requirement that a tool declares. A well-built tool does not silently assume a credential is present; it advertises, as part of its definition, the authentication scheme it needs (an API key, OAuth2 authorization-code or client-credentials, a cloud service identity, mutual TLS) and the minimal scopes those operations require. This declaration is what lets the framework resolve credentials automatically and lets a reviewer see, from the tool definition alone, exactly what permission the tool will exercise.
The credential broker is the component that turns that declared requirement into a usable token at call time. It is the only party trusted with long-lived material, and it exposes a narrow interface: given the current invocation context (which user, which agent, which tool, which scopes), return a concrete credential to inject. Centralizing this logic means secret handling, token caching, refresh, and rotation live in one audited place rather than being reimplemented — and gotten wrong — inside every tool.
Behind the broker sits the secret store or vault. Application secrets — OAuth client secrets, refresh tokens, service account keys — never live in code, environment variables baked into an image, or the tool config; they live in a managed secret store (a cloud secret manager, HashiCorp Vault, a KMS-backed store) that enforces access control, encryption at rest, and rotation. The broker reads from the vault under its own tightly scoped identity, so even the broker holds secrets only transiently and only when a call demands them.
The OAuth token exchange is the piece that delivers per-user identity. When a tool must act for a specific end user, the broker does not use a service account; it performs an on-behalf-of exchange, trading a token that proves the user's identity for a new access token scoped to the downstream API and carrying that user's consent. This is the standard OAuth pattern for delegated access, and it is what lets the agent read this user's email while being structurally unable to read anyone else's. For first-time consent, the flow may route the user through an interactive authorization step whose resulting refresh token the vault then stores.
The final piece is injection at the transport layer. The minted short-lived token is attached to the outbound HTTP request — typically an Authorization: Bearer header, sometimes a signed request — by the tool's transport code, not by the model. It never appears in the prompt, never in the tool's textual arguments, and never in the tool's textual result that flows back into the model's context. The external API or MCP server on the other end verifies the token signature, checks its scope and expiry, and enforces the identity it carries. From the model's perspective the call simply works; the entire credential lifecycle happened in components the model cannot read.
End-to-end flow
Trace a single authenticated tool call. A user asks the agent, 'What's on my calendar tomorrow?' The model reasons that it should call the list_calendar_events tool and emits a tool call with arguments for the date range. It emits no credential, because it has none — the tool call is pure intent plus arguments, exactly as the model was trained to produce.
The framework intercepts the call and consults the tool's declared auth requirement: OAuth2, scope calendar.events.readonly, acting on behalf of the end user. It hands this, along with the invocation context identifying the current user, to the credential broker. The broker first checks its short-lived cache for a live token matching this user and scope; on a miss, it proceeds to mint one.
To mint the token the broker reads the user's stored refresh token (or a user-identity assertion) from the vault under its own identity, then performs the OAuth on-behalf-of exchange against the identity provider, requesting only the calendar.events.readonly scope and a short lifetime. The provider returns an access token that carries the user's identity and that one scope, valid for a few minutes. The broker caches it keyed by user and scope so subsequent calls within the window skip the exchange, and returns it to the framework.
The framework injects the token into the outbound request the tool makes — as an Authorization: Bearer header on the HTTPS call to the calendar API — and invokes the tool. Critically, the token is placed on the request object, not merged into the tool's arguments or logged in plaintext. The calendar API validates the token's signature, confirms the scope permits reading events, confirms it has not expired, and enforces the user's own permissions: it returns only that user's events for tomorrow.
The tool receives the API response, extracts just the event data, and returns a clean, credential-free result to the model — a list of titles and times. The model reads that result and composes its answer. If the same user asks a follow-up seconds later, the broker serves the cached token; if they return an hour later, the cached token has expired, the broker transparently refreshes it, and the call proceeds. Throughout, the model orchestrated the what and the broker handled the authority, and at no point did a secret cross into the model's context. That clean separation is the entire design.