Big Blue Returns runs inside IBM Consulting Advantage Agent Studio, reaches an enterprise MCP server through ContextForge, and completes a refund with a credential that exists for five minutes. The agent decides. Identity and policy define what it can actually do.
The picture above is the whole argument, so here is how to read it. A customer signs in to the Big Blue Returns site through IBM Verify. The site stages that person's token with our MCP server and gets back a two-minute sessionRef. The agent running inside IBM Consulting Advantage uses that reference as an ordinary tool argument. ContextForge MCP Gateway governs the path from the agent to our tools. The enterprise MCP turns the reference back into real user context and builds a precisely scoped authorization request. IBM Verify issues an on-behalf-of token. Vault mints a Postgres credential that lives five minutes. The refund completes, the lease is revoked, and the system keeps evidence of every stage.
The interesting part was not teaching an agent how to issue a refund. It was proving that the hosted runtime never needed a permanent database credential to do it.
The agent makes the decision. The runtime makes the request. Identity determines the authority. Vault releases a credential only when the request is specific enough to deserve one.
Most of the agent deployments I get asked to look at share one habit. Somewhere near the agent sits a credential with more reach than any single action needs: a service account, a connection string, an API key with write scope. The agent is trusted with it because the agent has to do things, and the credential is the easiest way to let it.
That habit is where the risk lives. A prompt-injected agent with a standing credential can do anything the credential can. A compromised runtime with a standing credential is a compromised database. The credential does not care who asked.
So the design goal for Big Blue Returns was narrow. I did not want to make the agent more trusted. I wanted to make its authority smaller, more explicit, and easier to prove. The agent should be able to decide that a refund is warranted and ask for it. Whether the refund is allowed, for whom, for how much, and with what database access should be decided somewhere the agent cannot reach, on the strength of the signed-in user's identity, and enforced with a credential that does not outlive the request.
This post walks through the build that proves that is achievable with a hosted agent platform, not only with an agent you run yourself.
Big Blue Returns is a refund desk. A customer chats with it, gives it a receipt number, and it looks the receipt up, verifies the customer who owns it, and processes the refund. Nothing exotic. It is deliberately a boring business action, because boring business actions are where these patterns have to hold.
The agent itself, named bigblue-returns, runs inside IBM Consulting Advantage Agent Studio. It is a Strands orchestration on GPT-5.1, defined in a YAML file (refund-ica/ica/bigblue-returns-agent.yaml) and imported through the Agent Studio console. From the outside it is reachable through one path, the A2A protocol, which is how the branded Big Blue Returns web application talks to it.
What the agent does not have is a database. It has three tools, exposed through ContextForge MCP Gateway, and each tool call has to earn its access on the enterprise side.
This is the IBM Agentic Identity runtime-security pattern applied to a hosted agent: IBM Verify for identity and authentication, OIDC Authorization Code Flow with PKCE for sign-in, RFC 8693 token exchange for delegation, RFC 9396 Rich Authorization Requests to make the delegation specific, the act claim to name the acting agent, and HashiCorp Vault with the verify-rar plugin to turn all of that into a credential that expires. Earlier posts on this site build the same chain around agents I host myself. This one puts the agent on someone else's platform and keeps the chain intact.
Stated as a single sentence: a hosted agent does not need standing database access to complete a business action. It can act only on behalf of a real signed-in user, for one specific authorized action and amount, using a narrowly scoped credential that exists for five minutes and is revoked after use.
Every clause in that sentence maps to something you can see. "Real signed-in user" is the sub claim in the on-behalf-of token, and it is the person who logged in, not an integration account. "One specific action and amount" is the RFC 9396 authorization detail carried in that token. "Narrowly scoped credential" is a Postgres role Vault mints for the retail schema. "Five minutes" is the lease TTL. "Revoked after use" is a revocation call the MCP makes on both the success and the failure path, and the audit receipt records whether it ran.
I wanted each of those to be observable in the running system, not asserted in a diagram. Section 09 covers the evidence. The rest of the post explains how the pieces fit.
Here is the separation that makes the whole thing work. The agent runtime decides which tool it needs. ContextForge decides how that tool call reaches the enterprise. The enterprise MCP decides whether the call is authorized and what credential, if any, it gets. Three parties, three jobs, and none of them has to do another one's job.
Concretely, ContextForge MCP Gateway composes our MCP's tools into the ICA application's virtual server, so the agent sees lookup-receipt, lookup-customer, and process-refund as ordinary tools it can call. When the agent calls one, ContextForge routes the governed invocation to the enterprise MCP endpoint and authenticates to that endpoint with its configured bearer-token mechanism. It carries the tool call across the boundary. That is its role, and it is a valuable one, because it means the hosted agent never needs direct connectivity to a database or broad database credentials. It needs a governed connection point.
I want to be precise about what happens on each side of that seam, because it is easy to blur. ContextForge does not perform the RFC 8693 exchange, enforce the RFC 9396 authorization details, or mint the Vault credential. Those run inside the enterprise-controlled MCP service, which stays the identity-aware policy enforcement and credential-release boundary. ContextForge's contribution is to make a hosted agent's tool call arrive at that boundary in a governed, composable way. The integration is useful because it preserves a clean separation: the agent can ask, ContextForge can route, and the enterprise authorization layer can decide.
The pattern does not require every agent runtime to become an identity provider, a secrets manager, or a privileged database client. It requires a governed connection point and an enterprise policy service that can turn an agent request into an identity-bound, action-specific authorization decision.
That is also why the same ContextForge integration model is not specific to refunds. Any governed enterprise MCP service, API, system of record, or security decision point that can sit behind that seam can be reached the same way, with the same division of responsibility. I come back to this in section 11.
This is the full sequence for one refund. It reads long because every step is a distinct control, and I would rather show the whole chain than summarize it.
POST /session/stage.sessionRef.sessionRef into the A2A message it sends the ICA agent using message/stream.ak_ key.sessionRef unchanged as a tool argument.bigblue-returns-ars, routes the tool call to the enterprise MCP endpoint at ${MCP_ENDPOINT}.sessionRef back to the original user token.authorization_details that identify the precise refund action and the authorized amount.act claim identifying the Big Blue Returns agent as the acting workload.verify-rar plugin.retail schema.The question I kept coming back to was how the enterprise MCP should learn who the signed-in user is, given that the agent sits on a platform I do not run. The obvious answer, put the user's access token in the message and let the agent forward it, is the one I did not want. A bearer token in a prompt or a tool argument is a bearer token in every log along the way, and it hands the agent something it can replay.
So the token never travels with the agent. The browser session, which already holds it, stages it directly with the MCP over a private path and receives a reference in return. The reference is what the agent sees. It looks like this on the wire.
// A2A message from the Big Blue Returns UI to the ICA agent (sanitized) { "message": "Please refund order 84721 for $42.50.", "context": { "sessionRef": "${SESSION_REF}" } } a2a · message/stream
The reference is opaque and short-lived. It carries no identity of its own and it means nothing to IBM Verify, to Vault, or to any external API. It is useful to exactly one party, the enterprise MCP, which holds the matching staged token for two minutes. The agent forwards it as a tool argument and does not need access to the original user token to do its job.
When the agent decides a refund is warranted, the tool call it makes through ContextForge carries the business intent and the reference, nothing more.
// MCP tool invocation as it arrives at the enterprise MCP (sanitized) { "tool": "issue_refund", "arguments": { "orderId": "84721", "amount": { "currency": "USD", "value": "42.50" }, "sessionRef": "${SESSION_REF}" } } mcp · tools/call
Read that as a request for authorization, not as proof of it. Nothing in the tool call grants anything. The MCP resolves the reference, and if the reference is missing, expired, or does not match a token it staged, the call is refused. There is no fallback to a shared account, because a quiet fallback would make the whole property unprovable.
Because the reference resolves only at the MCP, only against a token the trusted UI staged, and only inside its window, an attacker cannot hand the MCP a reference of their choosing and have it act as someone else. Redeeming it never returns the underlying token to the caller either. The MCP uses the staged token internally for the exchange and returns only the tool result.
Once the MCP has the real user token, it does not use it to talk to the database. It uses it to ask IBM Verify for a much smaller token. That is the RFC 8693 token exchange, and the reason it matters is the part most integrations skip: the exchange request carries RFC 9396 authorization_details describing exactly what the resulting token is for.
// RFC 9396 authorization_details sent with the RFC 8693 exchange (illustrative) { "type": "refund", "actions": ["refund:create"], "resource": "retail/orders/84721", "amount": { "currency": "USD", "value": "42.50" } } rfc 9396 · authorization_details
The exact schema is implementation-specific. Ours names the action, the receipt, and the instructed amount in the shape the Vault plugin expects. The security principle is not implementation-specific at all: authorization is bound to one action, one resource, and one amount. A token minted for this refund is not a token for the next refund, or for a larger one.
IBM Verify evaluates the request against policy and returns an on-behalf-of token. Here is a sanitized view of the claims that matter.
// On-behalf-of token claims returned by IBM Verify (sanitized, illustrative) { "sub": "[email protected]", "act": { "sub": "agent:bigblue-returns" }, "authorization_details": [ { "type": "refund", "actions": ["refund:create"], "resource": "retail/orders/84721", "amount": { "currency": "USD", "value": "42.50" } } ] } rfc 8693 · on-behalf-of token
Three claims carry the whole story. sub is the user on whose behalf the request is happening. act is the agent, the workload that actually performed the action. And authorization_details says exactly what that actor may do for that user. When I first decoded a live one of these from the running demo and saw my own identity in sub, the agent in act, and a single refund with a single amount in the details, the design stopped being a diagram.
The act claim deserves a moment. It is how a downstream system can tell "this user did this" apart from "this agent did this for this user." Both are true here, and the token says so. That distinction is what lets an auditor answer the question every agent deployment eventually gets asked: who was acting, and on whose authority?
The on-behalf-of token is still not a database credential. What it carries is proof that a specific user, through a specific agent, is authorized for a specific action. The last step turns that proof into access, and it is the step that makes standing credentials unnecessary.
The MCP presents the token to HashiCorp Vault's verify-rar plugin. The plugin validates the authorization details against the role's constraints, and only then does Vault mint a Postgres role scoped to the retail schema, with a five-minute lease. In pseudocode, the shape of the credential path is this.
# Vault mints a role for this one call, # then the MCP guarantees revocation (pseudocode) cred = vault.read("${VAULT_ADDR}/v1/verify-rar/creds/${VAULT_ROLE}", obo_token) # → username: v-refund-write-… ttl: 300s # → lease_id: verify-rar/creds/${VAULT_ROLE}/… try: # role limited to the retail schema db = connect("${POSTGRES_HOST}", cred.username, cred.password) db.execute("UPDATE retail.receipts SET refunded_at = now()" "WHERE receipt_id = %s", receipt) finally: vault.revoke(cred.lease_id) # runs on success AND on error vault · verify-rar lease
Two details matter more than they look. The credential has a five-minute TTL, so even if revocation somehow failed, the exposure window is bounded. And revocation runs in a finally path, so the lease is released whether the refund succeeded or threw. A credential that is only revoked on the happy path is a credential that leaks on every unhappy one.
Put the three sections together and the property from section 03 holds without anyone having to trust the agent. Identity determines who may act. The exchange and the RAR determine what they may do. Vault releases a credential only when that request is specific enough to deserve one, and takes it back when the work is done.
A security architecture you cannot see is one you have to take on faith, and I did not want anyone to take this on faith. So the demo makes the chain observable rather than invisible. Next to the chat there is an Agentic Runtime Security Pipeline panel, and a token-receipt view fed by the MCP's audit endpoint.
For every refund it shows the subject token and the on-behalf-of token, both decodable in place; the agent identity in the act claim; the authorization details for the exact action and amount; the Vault lease metadata; and whether the credential was revoked. And it shows one small chip that I ended up caring about more than anything else on the screen: a green status reading Real user · staged.
It demonstrates that the refund did not run under a generic integration account or service identity. The system staged the real signed-in user context, then constrained the resulting authority to a specific action, amount, agent actor, and five-minute credential window.
The audit receipt behind that chip looks like this, sanitized and illustrative.
// One audit receipt from the ARS pipeline (sanitized, illustrative) { "traceId": "refund-transaction-id", "user": "[email protected]", "agent": "bigblue-returns", "sessionRefStatus": "resolved", "tool": "issue_refund", "refund": { "orderId": "84721", "amount": "42.50", "currency": "USD" }, "tokenExchange": "approved", "vaultLeaseTtlSeconds": 300, "credentialRevoked": true } ars · audit receipt
Every claim in section 03 has a field here. The real user is user, and its source is the sub of the exchanged token. The acting agent is agent, from act. The specific action and amount are refund, from the authorization details. The policy decision is tokenExchange. The bounded credential is vaultLeaseTtlSeconds, and credentialRevoked records that the finally path did its job. If any of those were missing, the property would be a claim instead of a fact.
What the running system demonstrates: a live refund whose on-behalf-of token carried a real user as sub, the agent as act, a single refund action and amount as authorization details, followed by a five-minute Vault credential and a recorded revocation. What is architectural interpretation: that the same seam and the same chain generalize to other systems of record. Section 11 makes that argument; the sections before it show the evidence.
For readers who want to place each piece, this is the deployment shape. Operational values are placeholders.
| Component | Location | Implementation notes |
|---|---|---|
ICA agent bigblue-returns | IBM Consulting Advantage Agent Studio | Strands orchestration; defined in refund-ica/ica/bigblue-returns-agent.yaml; imported through the Agent Studio console; invoked externally through A2A |
ContextForge gateway bigblue-returns-ars | ICA-hosted | Configured with bearer authentication; composes the MCP tools into the application virtual server; routes calls to the enterprise MCP |
| MCP server | Enterprise application host | refund-ica-mcp systemd service; port 3017; loopback-bound; TypeScript on tsx; deployed by rsync and a service restart |
| Branded UI | Enterprise application host | refund-ica-ui systemd service; port 5181; SvelteKit adapter-node; production build deployed by rsync |
| IBM Verify | IBM Verify tenant | OIDC authentication, token exchange, identity context, and authorization decisions |
| Vault and Postgres | Vault environment | verify-rar role refund-write; five-minute credentials for the retail schema |
On exposure: the MCP publishes only the surfaces the integration needs, /mcp for ContextForge and /.well-known for discovery. The staging endpoint, the REST tool path, and the audit endpoint (POST /session/stage, /tool, /me/audit) are restricted enterprise endpoints used by the application and the local runtime. The browser session stages its token over that private path, which is why the token never has to appear on the public route the agent's calls take.
Nothing in the chain is specific to refunds except the vocabulary in the authorization details and the Postgres role Vault mints. Swap those and the same architecture governs a different action against a different system.
That is the practical value of the ContextForge seam. A hosted agent platform can participate in enterprise-grade identity and authorization architecture without becoming a trusted holder of credentials for every system it touches. The platform provides the governed connection point. The enterprise provides the policy service that turns an agent request into an identity-bound, action-specific decision, and the credential broker that turns that decision into access with an expiry. Other governed MCP services, internal APIs, systems of record, and security decision points can sit behind the same seam with the same division of labor.
It also changes what you have to trust. You do not have to trust that the agent will only ever do the right thing, because the agent cannot do more than the exchanged token allows, and the token cannot outlive the request. You have to trust IBM Verify to decide, Vault to release and revoke, and the MCP to fail closed. Those are systems built to be trusted with exactly that, and they leave evidence when they do it.
The most important property of Big Blue Returns is not that it can process a refund. It is that the hosted agent never needs to become a privileged database client to do it.
ContextForge provides the governed connection. IBM Verify supplies identity and delegated authority. RFC 8693 and RFC 9396 make that authority specific. Vault turns it into a credential that expires before it becomes standing access.
The agent can make the decision. The architecture keeps the authority.
The live build is at ${DEMO_URL}. Sign in, refund a receipt, and open the pipeline panel. The green chip is waiting.
act claim, may_act)authorization_details)