Insights
MCP and AI agents: a safer tool starts outside the model
Model Context Protocol makes it easier to connect a model or agent to tools. The protocol by itself does not decide whether the agent should be allowed to perform a particular operation, on which resource, and with what consequence. The application layer still has to enforce those boundaries.
If an agent can use MCP to read a report, change configuration, start spending money or delete a resource, we treat it as a privileged API client. The prompt describes intent. The gateway enforces limits.
In practice, we design that boundary around seven principles.
1. Separate operator identity from target-system credentials
An agent acts on somebody's behalf. We therefore need to answer at least two separate questions:
- who requested this operation and what are they allowed to do in Brillnet?
- how does Brillnet authenticate to the external provider?
They are not the same thing.
In our internal Brillnet Ads MCP tool, the operator passes through Brillnet's authorization layer, while calls to Google use a separate service identity. The provider token is short-lived and is not returned through MCP as data for the model.
The model does not need to know a credential in order to perform an allowed task. It also cannot accidentally paste that credential into an answer if it never received it.
Rule
A secret should be available to the component that has to use it, not to the entire execution chain.
2. Express permissions by operation, not only by agent name
A role such as marketing-agent is easy to read, but on its own it says very little about what the system will actually allow.
It is more useful to separate permissions by consequence:
read
→ reporting and retrieval
write
→ create or modify a resource
operate
→ activate an operational consequence, such as spend or publication
destructive
→ delete, detach, revoke or make another difficult-to-reverse change
An agent can have read without write. It can have write to prepare a campaign but no operate, so it cannot enable that campaign. Deletion can require both write + destructive.
The names do not have to be exactly these. The important distinction is between preparing a change and activating its consequence.
Read: Least privilege for AI agents
3. Safe defaults must work without help from the model
If omitting one parameter can activate an action, the default is unsafe.
In our internal Brillnet Ads MCP, a new campaign remains PAUSED when no status is explicitly supplied. The detail looks small, but the rule is important: missing information is not consent to start spending.
The same pattern applies elsewhere:
- document with no status → draft, not publish;
- new user with no role → no access, not a default role;
- integration with no confirmed destination → no synchronization;
- ambiguous resource → error, not the “most likely” record;
- missing approval for a high-risk action → stop, not retry.
A safe default should remain safe when the model hallucinates, omits a field or misunderstands intent.
4. High-impact operations need a separate gate
An agent can prepare a technically correct change that is still the wrong business decision.
Spending budget, publishing content, messaging a customer, changing access or deleting data produces consequences beyond the agent session. Approval should therefore be an independent event.
At minimum, an approval preview should show:
- target system and resource;
- operation type;
- material parameters of the change;
- expected consequence;
- whether the operation is reversible;
- who is approving.
“Continue?” is not enough. The person needs to know what exactly is being approved.
Read: When should an AI agent require human approval?
5. A retry must not silently duplicate a write
Agentic systems make many API calls. Network errors, timeouts and retries are normal. The problem starts when repeating the same operation creates a second resource or executes a second payment, publication or message.
Where the operation supports it, we use an idempotency key. The gateway recognises the same attempt and prevents an uncontrolled duplicate write.
That should be combined with read-after-write:
agent requests a change
↓
gateway validates and performs the write
↓
target system responds
↓
gateway reads the resulting resource state
↓
operator sees that resulting state
A provider returning HTTP 200 is then not the only evidence that the system is in the expected state.
6. A raw gateway must not become a back door
Large APIs create a practical temptation: instead of building a dedicated tool for every endpoint, expose a more general REST capability.
That can be reasonable, but only if the general path inherits the same controls.
In Brillnet, method, endpoint and request characteristics are classified before execution. A low-level request does not bypass required functional scopes or the additional control for destructive operations.
The anti-pattern looks like this:
safe MCP tools
→ scoped and validated
raw_http_request
→ can call any endpoint with a full-access token
In that design, the previous controls are largely cosmetic. The broadest path defines the real security boundary.
7. The audit trail records the decision and outcome, not secrets
Our internal tool records request metadata and outcomes in an append-only integrity-controlled audit chain. Credentials, authorization headers and raw personal identifiers are excluded.
That distinction matters. An audit trail is supposed to reconstruct an action, not duplicate the security context into another data store.
For an agent operation we usually want to know:
- request/task ID;
- operator and agent identity;
- tool and operation class;
- target resource;
- authorization decision;
- approval ID if approval was required;
- provider/backend outcome;
- state observed after a write;
- policy or configuration version that applied.
Example architecture: an agent for Google Ads
Our internal case is deliberately narrow. The tool is used only for Google Ads accounts owned by Brillnet. It is not an agency service and does not receive access to customer ad accounts.
The flow can be simplified to:
Brillnet operator
↓
identity + profile + scopes
↓
agent / model
↓
MCP tool call
↓
gateway: validation + classification + approval policy
↓
short-lived provider token
↓
Google Ads API
↓
normalisation + redaction
↓
read-after-write / outcome
↓
audit trail
The most important control is not inside the model. It is the layer between intent and execution.
Convenient-looking anti-patterns
One administrator token for every agent
It makes the prototype easier. Later, it becomes difficult to limit the impact of an error or prove that an agent should have been able to access a resource at all.
“The model will ask the user if an action is risky”
That delegates the security decision to the same component whose mistakes the control is meant to contain.
Logging the full prompt and response as the audit trail
A conversation is not an operational audit record. It may also contain information that should not be copied into the logging system.
Retrying every error automatically
Not every error is transient. Not every write is idempotent. An unclassified retry can increase the damage.
A universal endpoint with full access “for future features”
If one broad tool can bypass the constrained tools, the broad tool is the actual permission model.
MCP server review checklist
Before production use, I would verify at least:
- Is operator identity known for every call?
- Are target-system credentials hidden from the model?
- Can read, write, operate and destructive permissions be separated?
- Does authorization restrict the resource/tenant/account as well as the function?
- Does an omitted parameter lead to a safe state?
- Do financial, external and destructive operations have independent approval?
- Are write retries controlled?
- Is the target resource read back after a write?
- Does any low-level gateway use the same authorization rules?
- Does the audit trail exclude tokens, secrets and raw personal data?
- Are arbitrary filesystem paths and unbounded payloads rejected?
- Can agent or operator access be revoked centrally?
MCP 2026-07-28 changes the protocol, not the need for application controls
MCP version 2026-07-28 introduced, among other changes, a stateless protocol core and additional authorization hardening. Those changes make the communication layer easier to scale and reason about. They do not decide the business policy for a deployment: whether an agent may spend PLN 5,000, delete an account or send a message to 10,000 people.
The application still has to enforce that decision.
Sources
- Model Context Protocol — 2026-07-28 Specification
- OWASP AI Agent Security Cheat Sheet
- OWASP MCP Top 10 — Insufficient Authentication & Authorization
- OWASP MCP Top 10 — Lack of Audit and Telemetry
Boundary
These are patterns we use in our own work. This page does not claim that the whole tool is certified or universally compliant with OWASP, NIST or the complete MCP specification. Each integration still needs its own threat model and security testing.
If you are assessing how Brillnet handles access, data and AI, see Security and Compliance and AI oversight.
