Building Data-Fetch MCPs for AI Agents: A Six-Dimension Design Framework
Introduction
When building tools for AI coding assistants like Claude Code, one of the most common requirements is connecting the agent to live data. You need it to query a database, fetch metrics from a service, or retrieve geospatial analysis results. This is where the Model Context Protocol (MCP) comes in — it is the mechanism by which AI agents call external capabilities.
At first glance, building a data-fetch MCP sounds straightforward: wrap your existing API, expose it as a callable tool, and you are done. In practice, though, a naively built data-fetch MCP frequently leads to runaway costs, unstable workflows, and agents that behave unpredictably. The root cause is almost always the same: the tool was designed for human consumers, not for an LLM.
This post is a deep dive into what it actually takes to build a data-fetch MCP well. We will walk through a six-dimension framework for thinking about the design, examine common pitfalls, and offer concrete patterns you can apply whether you are wrapping a REST API, a PostGIS database, or a proprietary analytics backend.
Background and Context
What MCP Is (and Is Not) in the Claude Code Context
MCP tools are not SDKs, not backends, and not BI dashboards. They are callable primitives exposed to the model. The model reads their schemas, decides when to invoke them, passes arguments, and consumes the returned data as part of its reasoning context.
This framing has critical implications:
- The model's goal is to accomplish a workflow, not to explore a dataset.
- Every tool invocation consumes tokens and potentially money — both from the API call itself and from the response added to the context window.
- The model is very good at using well-defined capabilities but surprisingly bad at exploring loosely defined ones.
So the design question is not "how do I expose my data?" but rather: "how do I make external data available in a way that is stable, cheap, structured, and semantically clear enough for a model to use correctly every time?"
The Common Starting Point
Many teams building data-fetch MCPs already have a solid backend: import pipelines have run, data has been cleaned and stored in a structured format, and internal query APIs exist. This is actually the ideal starting point. The hard part — data normalization, schema definition, and domain modeling — is already done.
The remaining work is not re-implementing data access. It is adding a model-facing interface layer: semantic parameter design, output schema contracts, cost guardrails, and security boundaries. Framed this way, building an MCP on top of an existing backend is mostly a matter of careful API design rather than engineering complexity.
Core Concepts
The Six Dimensions of Data-Fetch MCP Design
Every design decision in a data-fetch MCP can be traced back to one of six dimensions. Skipping any of them tends to produce specific, predictable failure modes.
Dimension 1: Data Source Type
The first question is what you are actually fetching from. Different source types have different challenges and impose different constraints on your MCP design:
| Source Type | Key Characteristics | Primary Design Concern |
|---|---|---|
| REST API | Most common, external | Rate limiting, pagination, parameter mapping |
| GraphQL | Flexible query surface | Prevent runaway query depth; token explosion risk |
| File-based (CSV, JSON, Parquet) | Potentially large | Slicing, sampling, streaming |
| Database (SQL/NoSQL) | Powerful but risky | Query templating, allowlists, read-only enforcement |
| Web scraping | Unstable, unstructured | Generally not recommended for direct MCP exposure |
In practice, roughly 80% of data-fetch MCPs in Claude Code deployments wrap either REST APIs or database query layers. The choice of source type determines which risks you need to engineer around.
Dimension 2: Invocation Granularity
This is the most consequential design dimension and the one most commonly underestimated.
The instinct for engineers familiar with general-purpose APIs is to build something flexible:
// Flexible but dangerous
fetch_data(table: string, where: string, limit: number)This is a anti-pattern for LLM tools. Here is why: models do not have stable heuristics for how to use open-ended parameters. They will generate inconsistent queries, occasionally produce expensive or nonsensical ones, and the results cannot be reliably composed into downstream workflow steps.
The correct approach is to encode your domain knowledge into the tool's interface:
// Semantically constrained and model-friendly
get_coverage_score(
location_id: string,
radius_meters: number,
as_of_date: string
)The rule of thumb: if you, as a domain expert, already know the canonical way to ask this question, that knowledge belongs in the MCP schema — not in the model's prompt.
This is not about limiting capability. It is about translating tacit domain expertise into explicit tool contracts that the model can reliably act on.
Dimension 3: Cost and Token Control
In a Claude Code context, an unconstrained data-fetch tool is a liability. The model may call it repeatedly across turns. A single call returning a large dataset can blow out the context window. Aggregate API costs can spiral unexpectedly.
The minimum viable set of controls includes:
- Hard row limits: Never return more than N rows, regardless of what the caller requests
- Field projection: Allow — or enforce — returning only specified columns
- Time range constraints: Refuse queries spanning more than a defined window
- Pagination with explicit page size: Never let a single call paginate through thousands of records
A concrete example:
{
"max_rows": 500,
"fields": ["id", "location", "score", "timestamp"],
"date_range_days_max": 30
}Without these guardrails, you will face a dual failure mode: API cost overruns from the upstream data service, and context overflow degrading the model's ability to reason coherently.
Dimension 4: Reproducibility
A data-fetch MCP is not just used interactively — it becomes part of automated workflows that may run across multiple sessions, generate outputs used in reports, or be audited later. This creates a requirement that is unusual for typical APIs: given the same input, the output structure and semantics must remain stable over time.
Reproducibility requires:
- Fixed output schema: Field names and types do not change between calls
- Explicit versioning: Both the data version and the query/algorithm version should be capturable
- Temporal anchoring: When returning time-sensitive data, always include an
as_oftimestamp in the response
Without this, a workflow built today may silently produce different results tomorrow, making debugging and auditing nearly impossible.
Dimension 5: Security and Access Boundaries
Because the model decides autonomously when and how to invoke MCP tools, the attack surface differs from a human-operated API. The model may be prompted by injected content, may misinterpret ambiguous instructions, or may be tested with adversarial inputs.
The practical controls:
- Never expose raw SQL passthrough: A
sql: stringparameter is a prompt injection vector waiting to happen - Use query templates with typed parameters: The model picks from a defined vocabulary of query shapes
- Enforce read-only at the connection level: Not just by convention, but at the database credentials level
- Allowlist query types explicitly:
query_type: "monthly_summary" | "location_stats" | "coverage_report"A useful heuristic for deciding what to expose: if you would not give this endpoint to an external contractor without a legal agreement, do not expose it via MCP.
Dimension 6: Invocation Timing
The final dimension is conceptual rather than technical: knowing when not to use a data-fetch MCP is just as important as knowing how to build one.
MCP is the right tool when:
- The data is dynamic or stateful — its value changes between sessions
- The data does not fit in context without being wasteful
- The capability needs to be reused across multiple workflows
MCP is the wrong tool when:
- The data is already in the context window from a previous step
- The answer can be derived by reasoning from existing context
- The data is one-time use with no reuse potential across sessions
Violating this last dimension leads to a subtler failure than the others: an agent that makes unnecessary external calls, runs slowly, and incurs costs for no reason.
Analysis
The Existing Backend Advantage
Teams that already have a mature data backend — with normalized schemas, domain-specific query functions, and stable ETL pipelines — are in a structurally better position to build good MCPs. Here is why.
For most developers, the hardest parts of MCP design are:
- Deciding what the semantic units of data access should be
- Stabilizing the output schema
- Defining the domain vocabulary
If your backend already has named query functions like get_monthly_aggregates() or fetch_location_coverage(), those are already the semantic primitives. You have done the hard conceptual work. The MCP is just a translation layer — renaming, narrowing parameters, and enforcing output contracts.
What this means in practice is that the effort to go from "existing backend" to "good MCP" is mostly a design exercise, not an engineering one. You should budget time for:
- Auditing existing query functions to identify which are MCP-ready, which need parameter narrowing, and which should never be exposed
- Writing output schemas first — before writing code, define the JSON contract for what each tool returns
- Writing tool descriptions for the model — unlike documentation written for humans, these need to be unambiguous, free of jargon, and precisely define when the tool should be invoked
A Practical Triage Framework
When you have an existing backend and want to build MCPs incrementally, a useful first step is to classify each query function:
Tier 1 — MCP-ready: The function takes well-typed parameters with narrow semantics, returns a structured, bounded result, and is safe to call in read-only mode. Wrap it directly.
Tier 2 — Needs narrowing: The function is semantically correct but takes too many optional parameters or returns too much data. Write an MCP-facing wrapper that enforces constraints and projects to a minimal output schema.
Tier 3 — Never expose: The function returns sensitive data, executes expensive joins, or operates in a way that is not safe to expose to an autonomous agent. These stay internal.
In a typical backend, roughly 60-70% of query functions fall into Tier 1, 20% into Tier 2, and 10% into Tier 3. This means the path from backend to MCP is faster than most teams expect — but only if you do the triage first rather than exposing everything wholesale.
The Layered Architecture Pattern
Once you are beyond a single MCP tool, a natural architecture emerges:
Data Sources
↓
ETL / Import Layer (not touched by MCP)
↓
Internal Query API (existing functions)
↓
MCP Facade Layer (semantic wrapping, cost controls, schema enforcement)
↓
Model (Claude Code agent)
The key insight here is that the MCP layer is thin. It does not contain business logic, does not join tables, and does not perform derived computations. Those responsibilities belong either in the internal query layer (which can be tested and evolved independently) or in the model's own reasoning.
This separation makes the system maintainable: you can evolve your backend independently of your MCP interface, and you can update your MCP interface without touching core business logic.
Implications
For Tool Design in General
The six-dimension framework applies beyond data-fetch MCPs. Any tool exposed to an AI agent benefits from the same thinking: what is the semantic granularity, what are the cost and token implications, what security boundary does this introduce, and when should the model use it?
The shift from "API for humans" to "primitive for agents" is subtle but important. Humans bring context, judgment, and the ability to recognize when an API is returning garbage. Models bring consistency, composability, and the ability to chain operations — but they need the interface to carry the semantic load that humans supply implicitly.
For Workflow Architecture
A well-designed data-fetch MCP becomes a reusable building block across multiple workflows. This is where the real leverage comes from. If you invest in making a coverage-score tool semantically clean, cost-controlled, and reproducible, it can be used in analysis workflows, reporting workflows, monitoring workflows, and exploratory workflows without modification.
This reusability is what distinguishes a data-fetch MCP that was built thoughtfully from one that was bolted together quickly. Both may work initially. Only the former scales to a complex multi-workflow system without accumulating technical debt.
For Teams With Existing Backends
The most actionable takeaway for teams with mature backends is: you do not need to build a new API to build a good MCP. The data infrastructure you already have is likely a suitable foundation. The investment is in the design layer — the semantic contracts, the cost guardrails, and the security boundaries.
Start by writing the JSON schemas for the tools you want to expose. Before writing any code, validate that those schemas feel right: are the parameter names unambiguous? Is the output bounded? Would a model reading the description know precisely when to invoke this tool?
If the answer is yes, the implementation is the easy part.
Conclusion
Building a data-fetch MCP is deceptively simple to start and surprisingly easy to get wrong. The technical barrier — connecting to an API or database — is low. The design challenge — making that connection safe, stable, cost-controlled, and model-friendly — is where the real work lies.
The six-dimension framework provides a systematic way to approach this challenge:
- Data source type determines the baseline risks and constraints
- Invocation granularity determines whether the model can use the tool reliably
- Cost and token controls determine whether the system is safe to run autonomously
- Reproducibility determines whether the tool can be used in auditable, long-running workflows
- Security boundaries determine whether the tool is safe to expose to an autonomous agent
- Invocation timing determines whether the tool adds value or just adds overhead
Teams with existing backends are in a strong position: the hard conceptual work of data modeling is already done. The path to a good MCP is primarily a design exercise — audit your existing functions, write schemas before code, and resist the temptation to expose general-purpose query surfaces to the model.
Done right, a data-fetch MCP is not just a data connector. It is a stable, reusable AI primitive — a building block that multiple workflows can depend on to behave consistently and predictably, session after session.
