The moment you connect an MCP server, your coding agent stops being a thing that reads and writes in your repo and becomes a thing that can reach out and act. Read a database, hit an API, touch a service—that's the entire appeal, but it’s also the single biggest security problem we face today.
We talk a lot about RAG pipelines and complex prompt engineering. We assume that if the model knows what to do, it will execute safely. I spent years building high-performance systems in PHP/Laravel, dealing with race conditions and data integrity at scale. The transition to agentic workflows feels like going from managing database transactions to giving away a credit card.
The problem isn't the capability; it’s the guaranteed contract of that capability. Most AI tooling—and I mean most of what lands on GitHub today—treats data schemas as simple JSON representations, resulting in brittle, leaky, and architecturally unsound MCP servers.
My team built Vinkius partly to solve this exact problem: how do you enforce a rigorous engineering discipline onto something fundamentally opaque like an LLM? The answer required building the MCPFusion Developer Prover. It’s less of a code generator and more of a conceptual guardrail.
Why Raw Zod Schemas Are Not Enough for Agentic Contracts
The first thing that trips up any agent trying to interface with MCPFusion is data modeling. When an LLM defaults to what it knows, it reaches for raw z.object() definitions in a tool file. And this pattern is fundamentally dangerous.
A simple schema like: const UserSchema = z.object({ name: z.string(), email: z.string() }) might look fine on paper. But within the context of MCPFusion, that definition loses half its meaning and all its safety features.
The moment you bypass defineModel() for entity schemas, you lose critical business logic hooks:
- Security: No
.hidden()mechanism means sensitive data (password hashes, internal API keys) can leak into the schema exposed to the agent. This is a non-negotiable security violation. - Contextual Integrity: You lose
.fillable(), which allows you to differentiate between creating an entity and updating it (e.g., allowing certain fields only duringUPDATE). - Time Management: Automatic timestamps (
m.timestamps()) are crucial for auditing, but raw schemas ignore this. - Aliasing: The ability to use
.toApi()for clean alias resolution is lost in the schema definition itself.
The MCPFusion Developer Prover catches this immediately with a 'Raw Schema Detected' verdict. It forces you back into using defineModel(User).casts(...), which isn't just about naming fields; it’s about activating an entire layer of built-in enterprise safety features that plain Zod lacks.
The Egress Problem: Why Presenters Are Not Optional
A lot of developers (and agents, when prompted poorly) will use .handle() and then return raw data structures—a simple { users: [...] } object. This is the most common point of failure regarding security and user experience.
The problem here isn't just that you are returning JSON; it’s what kind of JSON, and whether or not your agent can confidently act on it.
When a tool returns raw database objects without attaching a Presenter, two things happen:
- Data Leakage: Internal fields—like soft-delete flags or unmasked internal IDs—are exposed to the client/agent, even if they shouldn't be seen. The Presenter is the designated egress gate.
- Missing Intelligence: You lose all UI intelligence: agent suggestions (
suggestActions), collection limits (agentLimit) for performance throttling, and server-rendered UX context (likeechartsdata structures).
The .returns(Presenter) contract forces you to define a layer that handles the final validation, stripping of hidden fields, and shaping of the response for consumption. If your agent is supposed to generate an actionable summary or display complex charts, that logic belongs in the Presenter, not scattered across raw handler return values.
Semantic Verbs: The Agent's Understanding of Intent
The next layer of complexity—and arguably the most philosophical part of building reliable agents—is understanding intent. An LLM doesn’t inherently understand if a request is destructive or merely informational. We have to teach it using semantic verbs.
Using f.action() for everything, purely out of habit, throws away critical operational guarantees:
-
f.query(): This signals read-only intent (GET). The system knows this operation can be safely cached, parallelized across multiple agents without race conditions, and retried if network instability hits. -
f.mutation(): This screams "destructive action" (POST/PUT/DELETE). Agents are forced to treat these calls with high caution—they must confirm the intent before execution, knowing that data integrity is at stake. -
f.action(): The neutral zone. Used for calculations or validations that change state but aren't traditional CRUD operations.
The MCPFusion Developer Prover doesn't just check if you used a verb; it checks if the verb matches the side effect. Using f.mutation('logs.search') because 'searching' sounds powerful is semantically wrong. Searching logs is fundamentally read-only—it should be f.query(). This enforcement teaches agents to think like database transactions, not just keyword matchers.
The Discipline of Separation: MVA as a Contract, Not Folders
The biggest architectural mistake I see people make when adopting this pattern is confusing the concept with the folder structure.
MVA (Model-View-Agent) separation must be understood as a conceptual contract. It’s about responsibilities.
- Models: Define data shape and constraints (
defineModel()). - Presenters: Handle egress, transformation, and validation of the output. They are the guardrails for what leaves the system boundary (the View).
- Tools/Agents: Expose specific functionality to the outside world (the Model-View contract).
The MCPFusion Developer Prover enforces this rigorous separation. It's not enough to put your model in models/. You must ensure that Models define data shapes, Presenters handle egress via .returns(Presenter), and Tools use semantic verbs correctly.
When I first started building MCPFusion, the goal was simple: stop agents from functioning like poorly typed JSON dumps. The resulting framework is a system of structured reflection—it forces adherence to these decision pivots every time you implement a tool or feature.
If you are serious about deploying autonomous workflows and connecting your agentic core to external APIs (and it sounds like we all are), understanding the rigor behind this architecture isn't optional. It’s mandatory for reliability, security, and scalability. You can see how this structured approach is validated against real-world mistakes at https://vinkius.com/mcp/mcpfusion-developer-prover.
Building robust agent pipelines means embracing the discipline of defining not just what data you need, but how that data must pass through layers of validation and intent declaration. That conceptual overhead is exactly what makes this framework powerful.
(A quick side note for those following along: The best part about having a strict architecture like this is knowing that even if your agent fails (and it will, because LLMs hallucinate), the failure mode is contained and predictable. You know whether you are leaking an internal ID or simply failing to calculate tax correctly.)
MCPs are the music of AI Agents. We built the catalog. Discover Vinkius MCP Catalog.
Top comments (0)