An MCP server is a trust boundary, not just a tool catalog
Connecting an MCP server exposes executable capabilities to a model. Discovery policy, call-time authorization, result handling, and audit decide whether the connection is safe — not the tool list.
Model Context Protocol makes connecting tools to a model feel like installing a catalog: point a client at a server, read the tool list, and the model can call each entry. That ease is real, and it is also why the metaphor misleads. A catalog is inert — nobody acts on your behalf because you browsed one. An MCP server, by contrast, is a service that executes operations against real systems whenever the model selects a tool, with whatever identity and permissions the connection carries.
The productive way to reason about an MCP server is as a trust boundary: a line where requests produced by a model — working from context assembled out of user input, retrieval, and previous tool output — cross into systems that hold authority over data, money, and customer state. Tool count and descriptions describe the interface. The properties that decide whether the connection is safe live in how you govern that line: which tools become visible, which identity executes each call, how results re-enter the conversation, and what you can reconstruct afterwards.
This post works through that boundary: what the MCP specification assigns to each side, the four decisions every connection forces, a support-agent example that shows where the boundary holds and where it fails, and where AIVAX — which acts as the MCP client for gateway inference — sits in the picture, including what it deliberately does not decide.
The catalog view hides who executes what
When a gateway connects an MCP source, the client lists the server's tools and converts each schema into a function the model can call. From that moment, tool names and descriptions are part of the model's decision surface: the model chooses when to call and with which arguments based on that text. At call time, the server executes against whatever backing system it holds — a database, a payment provider, a ticketing queue.
The catalog framing makes both halves look symmetric and safe: discovery reads like browsing, and calls look like lookups. They are not. A write-capable tool — create, update, refund, send — is an API endpoint wired to production state, invoked by a process whose context the server does not control.
The clients built by major labs reflect this asymmetry. OpenAI's remote MCP guide documents that the Responses API defaults to requiring approval of each MCP tool call and lets you skip approvals only once you decide the server is trusted, with an allowed_tools parameter that imports just a subset of a server's tools. Anthropic's MCP connector supports allowlisting, denylisting, and per-tool configuration. These are integration-layer trust controls, and their existence is the tell: the tool list alone answers neither who may call what nor with whose identity.
The protocol splits responsibility; it does not remove it
The MCP tools specification distributes the work across both sides of the wire. Servers own the enforcement point: implement proper access controls, rate-limit tool invocations, and sanitize tool outputs before results travel back. Clients own the human and transport edges: present confirmation prompts for sensitive operations, show tool inputs to the user before they cross the wire, validate results, enforce timeouts, and log tool usage for audit.
The authorization specification goes further and casts the server as an OAuth 2.1 resource server. Servers must validate that access tokens were issued specifically for them — audience binding, supported by RFC 8707 resource indicators — and reject tokens minted for another service, because forwarding unmodified tokens (token passthrough) is explicitly forbidden as a confused-deputy vector. Clients must implement PKCE and resource indicators when obtaining tokens; proxies must implement per-client consent. The security best practices document adds the surface around the boundary: SSRF protections when a server fetches URLs — including cloud metadata endpoints — session-hijack scenarios where injected instructions ride an existing session, compromise of local servers, and scope minimization so a leaked token stays small.
Read together, these documents describe a handshake of obligations, not a service that enforces authorization for you. The protocol tells each side what it must do; your application still decides what "allowed" means.
Four decisions the boundary forces
Every connection forces the same four decisions, and each belongs to a different phase of the request:
| Phase | The question | Where it belongs |
|---|---|---|
| Discovery-time policy | Which tools from this server become visible to this agent, and does the metadata still describe what they do? | Integrator and server: tool selection, inventory, review when behavior changes. |
| Call-time authorization | Whose identity, scopes, and approvals apply to this specific call? | MCP server and application: identity resolution, scope checks, approval rules, idempotency. |
| Result handling | Is this output data or an instruction, and what may re-enter the model context? | Client and server: sanitization, treating content blocks as untrusted input. |
| Audit and replay | Can you reconstruct who did what, approved by whom, and reverse or replay it? | Application and server: call logs with user, source, nonce, approver, and arguments. |
Two of these phases are routinely collapsed into one and should not be. Discovery-time policy is coarse and changes slowly — AIVAX caches tool discovery for 600 seconds by default, so a policy edit does not take effect immediately. Call-time authorization is per call and must be enforced on every request, because visibility is not permission: a model that can see a tool will eventually attempt to call it. By the same logic, a confirmation the model can answer by itself is not an approval. An approval is a decision by an identity the server independently recognizes.
A support agent makes the boundary concrete
Consider a support gateway connected to an internal MCP server exposing three tools:
lookup_customer_by_email— reads a customer record; read-only, but still subject to authorization and data minimization.create_support_ticket— opens a ticket; write-capable, low blast radius.issue_refund— moves money; write-capable, high blast radius.
Discovery-time policy separates them immediately. The lookup is read-only, but a wrong call can still expose another customer's data. It should be visible only to agents that need it, authorize each lookup against the acting user, and return only the fields required for the support task. Ticket creation stays restricted to agents handling support queues. The refund tool is either scoped to agents authorized for refunds or hidden behind a dedicated server, because with money movement you assume the model will eventually propose the call.
Call-time authorization is where the refund either holds or fails. When the model proposes the call, what crosses the boundary is a proposal, not a decision:
{
"jsonrpc": "2.0",
"id": 41,
"method": "tools/call",
"params": {
"name": "issue_refund",
"arguments": {
"payment_id": "pay_8f21c",
"amount_minor": 4200,
"reason": "duplicate_charge",
"idempotency_key": "refund-pay_8f21c-4200"
},
"_meta": {
"_aiv_external_user_id": "agent-7734",
"_aiv_call_source": "WebChatClient",
"_aiv_conversation_token": "cnv_01J9Q8V2",
"_aiv_nonce": "nonce-hash"
}
}
}
Nothing in that JSON authorizes anything. The arguments are what the model wants; the _meta block is the context the server uses to make its own decision — resolve agent-7734 to a role, check the refund policy, validate the nonce when request authenticity matters, and only then execute. The idempotency key in arguments belongs to that same layer: retries happen, and a refund that double-applies on retry is not authorized, it is unguarded. When the server's answer is no, it returns a readable error, because the model needs to know whether to ask the user for something else or stop.
Result handling closes the loop. The refund confirmation re-enters the conversation as text the model will reason over, which is exactly why the specification says to sanitize outputs and treat results as data: a compromised or malicious server can shape that text as instructions — the session-hijack pattern the security document describes. And audit turns the boundary reviewable: arguments, user, source, nonce, the server's decision, and which approval rule fired — enough to replay the incident instead of arguing about it.
Where AIVAX sits, and what it does not decide
AIVAX acts as the MCP client for gateway inference: it connects to a configured MCP source over Streamable HTTP, lists tools, converts each schema into a model-callable function, and executes the remote call when the model selects that tool. Authentication to the server is configured through custom headers, discovery is cached with a default of 600 seconds, and every call forwards metadata a server can authorize on: _aiv_external_user_id for user-based decisions, _aiv_call_source to distinguish web chat from API integrations, _aiv_conversation_token for correlation, and _aiv_nonce for request validation. The MCP configuration reference documents this behavior in detail.
That is transport, schema conversion, and metadata plumbing. The decisions across the boundary stay with your server and your application. AIVAX does not provide per-call human approval, per-tool allowlists, OAuth scope management, sandboxing, or automatic authorization — and its platform documentation says so directly: validate authentication, authorization, and expected arguments on the receiving side, and do not expose write-capable tools to untrusted users without application-level approval rules. If your refund requires a human yes or no, that approval flow is an application component, not a gateway parameter.
What this means for server authors and integrators
For server authors, the consequence is position, not paperwork: the MCP server is the enforcement point, so identity resolution, authorization, idempotency, approvals, and audit logs belong there. Restrictive schemas and specific tool names reduce wrong calls — AIVAX's documentation recommends exactly this — but they shape model behavior; they do not decide permissions.
For integrators, every connection is a third-party trust decision, with the operational facts that decision implies. Servers can change tool behavior without notice; OpenAI's documentation warns that MCP servers may update tool behavior unexpectedly, with unintended or malicious results. Data-handling terms differ by provider: OpenAI logs MCP request data for 30 days unless Zero Data Retention is enabled, and Anthropic's connector is explicitly not covered by ZDR arrangements. Both considerations push in the same direction OpenAI's own example does — prefer servers operated by the vendor that owns the system, and review what crosses the boundary, not just which tools exist.
Engineer the boundary, not the catalog
MCP succeeded at normalizing the plumbing: discovery, schema, transport, and call metadata now follow one contract, and AIVAX uses it to turn any compliant server into gateway tools. The same contract makes the division of labor explicit — the protocol distributes obligations across client, server, and application, and enforces nothing on your behalf. Treat each MCP server as a trust boundary with four governed phases — what becomes visible, who may call, what results mean, and what you can reconstruct — and the catalog becomes what it should have been all along: an interface description, not an architecture.