Start with the effect
An agent that drafts a plan and an agent that changes a customer record have different failure costs. Treating them as the same integration is an architecture mistake. Before choosing a model, describe the effects it may cause: create a task, update a document, send a message, deploy software, or move money.
For each effect, define the actor, workspace, resource, allowed operation, and review requirement. The important unit is a business action. A tool named execute_sql hides its meaning from reviewers; a tool named propose_customer_merge makes the intended change visible.
The MCP tools specification describes how a model discovers and calls tools, and recommends human visibility and control. Our architectural recommendation is to put enforcement behind the tool interface, in the same service that validates a human request. A model’s explanation is useful context; it is not an authorization decision.
Separate intent from permission
A helpful response might contain an instruction to “clean up inactive accounts.” That phrase is too broad to authorize a destructive operation. Resolve it into an exact proposal: which accounts, which expected versions, which action, and why each account qualifies. Then ask the policy layer whether this actor can perform that proposal.
// Illustrative application contract
type EffectProposal = {
workspaceId: string;
actorId: string;
operation: 'archive_account';
resourceId: string;
expectedVersion: number;
reason: string;
};
// The server supplies actor identity from authentication.
// It checks scope and permissions again at execution.
const decision = await authorize(actor, proposal);
if (decision.requiresReview) return queueForReview(proposal);
return executeWithVersionCheck(proposal);Never accept a model-supplied actor identifier as proof of identity. Bind authentication at the request boundary, and compare the requested workspace with the authenticated actor’s grants. A retrieved document, issue comment, or website can contribute facts. It cannot increase the agent’s permissions.
Database isolation is another layer. PostgreSQL row security can restrict rows for ordinary roles, but owners and roles with bypass privileges need special consideration. The application still needs explicit authorization for operations such as sending messages or invoking a deployment provider.
Review a concrete proposal
A review screen should show the actual change a person is approving: destination, affected records, external recipients, and the before-and-after state. “Allow the agent to continue?” is a weak approval because the reviewer cannot bound its meaning.
Store an immutable proposal and bind approval to its digest. If the agent changes the recipient or expands the record list, the previous approval no longer covers the new proposal. If a record version changes before execution, return a conflict and regenerate the proposal. Approval should not silently authorize overwriting someone else’s more recent work.
Separate execution from acceptance. An agent may prepare a patch and attach evidence, but the same actor should not be able to manufacture independent review. Make the review state part of the domain model, rather than an optional instruction in a system prompt.
Recover from uncertainty
External calls introduce ambiguous outcomes. If a provider times out after accepting a request, retrying with a new identifier can create the effect twice. Persist the intended operation and reuse a stable business identifier on retries. When the provider supports idempotency, send that identifier with the request.
When it does not, define a reconciliation path. Query the provider for a receipt or matching resource before issuing another write. Some outcomes must remain “unknown” until reconciliation finishes; changing that state to “failed” because a UI needs a tidy answer can lead to duplicate actions.
- Record the exact proposed effect before dispatch.
- Keep the provider receipt, effect identifier, and attempt history.
- Distinguish a rejected request from an uncertain provider outcome.
- Offer a human handoff when automatic reconciliation cannot establish the result.
Evaluate the boundaries
Evaluate more than answer quality. Build scenarios in which a document claims administrator permission, an agent requests a neighbouring workspace’s record, a tool response contains an instruction, and a provider response disappears after committing the effect. The system should reject unauthorized requests even when the model confidently asks for them.
Measure useful completed work alongside incorrect effects, review burden, recovery time, and cost. A system that avoids all work is safe in a trivial sense but commercially useless. Start with one bounded workflow, understand its failure modes, and expand authority only when the evidence supports it.
Working on a problem like this?
We help teams turn architecture decisions into working software.
Talk to the studio