When people first connect a language model to external data they usually write glue: a function that builds a URL, parses a response, and hands the result back as text. It works, and it is exactly the part that breaks whenever the API changes.
The Model Context Protocol exists to remove that glue. Instead of teaching each agent how to call each API, the API describes itself in a form agent runtimes already understand, and the tools simply appear.
The same data, two doors
An MCP server and a REST API can serve identical data under identical terms. The difference is who is expected to be holding the other end.
REST assumes a developer. You read documentation, you construct requests, you handle responses. It is universal, debuggable with a single command, and works from any language.
MCP assumes an autonomous consumer. The runtime fetches the tool list, reads each tool's parameters and description, and the model can then invoke them as native capabilities. No URL construction, no response parsing written by hand.
Neither replaces the other. A team building a dashboard wants REST. An agent deciding at runtime which of sixteen spatial tools answers its current question wants MCP.
How the protocol actually works
The mechanics are simpler than the acronym suggests, and knowing them makes the design decisions obvious.
A client connects to a server and asks what it offers. The server returns a list of tools, each with a name, a human-readable description, and a schema describing its parameters. The model reads those descriptions the same way it reads any other context, and when it decides a tool is relevant, it emits a call with arguments matching the schema. The runtime executes the call, returns the result, and the model continues reasoning with the answer in hand.
Two things follow from that loop, and both matter more than they first appear.
The description field is not documentation, it is the interface. The model chooses tools by reading those sentences. A tool described as returns place data will be selected almost at random. A tool described as returns businesses and points of interest inside a bounding box, sampled, with per-record licence, will be selected when it fits and ignored when it does not. Writing those descriptions is prompt engineering wearing an API designer's hat.
The parameter schema is a contract the model has to satisfy without a human checking its work. Every ambiguity becomes a failed call. Units belong in the parameter description, ranges belong in the schema, and defaults should exist wherever a sensible one does.
Naming is part of the interface
Tool names carry more weight than in a normal API because they are the first thing a model reads and sometimes the only thing it reads carefully.
Prefix consistently so a model can tell your tools from the other servers connected alongside them. A runtime may have four servers loaded, and a bare name like search invites collisions and misselection.
Name the answer, not the implementation. A tool called query_postgres_places tells the model about your infrastructure. A tool called find_places_in_area tells it what question the tool answers.
Keep the granularity coarse enough to be chooseable. Splitting one concept across five near-identical tools forces the model to distinguish between things it has no basis to distinguish, and it will pick badly.
Why geospatial data suits MCP unusually well
Spatial questions are compositional in a way that rewards tool discovery.
Answering is this warehouse location viable is not one query. It is: what is at this point, what roads reach it, what is the traffic pattern, what hazards have historically affected this extent, and what is nearby that competes. A human developer would wire those calls in a fixed sequence. An agent can decide the sequence based on what the previous answer revealed, and abandon branches that turn out to be irrelevant.
That only works if the agent can see the full menu and understand each item well enough to choose. Which is precisely what a tool manifest provides.
A walkthrough
Consider an agent asked to assess flood exposure for a distribution centre.
It begins by listing hazard events, because that tool's description mentions active events with extents. It finds a flood with a bounding extent that contains the site. Nothing yet is a conclusion: the extent is a modelled box, not the water's edge, and a well-written tool description says so.
Next it requests a brief for that event, which joins the extent against places, lifelines and terrain. The response includes exposure counts, each with a confidence, and a caveats array explaining that roads inside the extent are presumed impassable rather than observed to be.
Now the agent has a decision to make that a fixed pipeline could not. The exposure figure carries low confidence, so instead of reporting it, the agent calls site intelligence on the specific coordinate for a closer read, and separately checks road geometry to see whether the access route crosses the extent at all.
The final answer cites both figures, states which is measured and which is modelled, and repeats the extent caveat. That last part only happens because the caveat was in the response rather than in a documentation page the model never read.
What a geospatial MCP server should expose
Two design decisions matter more than the rest.
First, curate. A catalogue of a thousand endpoints exposed as a thousand tools does not empower an agent, it drowns it. Model selection degrades badly as the option count grows, and beyond a few dozen tools the failure mode shifts from choosing wrongly to not choosing at all. A focused set of well-described tools outperforms an exhaustive one.
Where a large catalogue genuinely needs to be reachable, the better pattern is two meta-tools: one that searches the catalogue by keyword and returns matching identifiers with descriptions, and one that fetches a specific item by identifier with its parameters. The agent discovers in one call and fetches in the next. A thousand capabilities become reachable through two slots in the tool list, and the model's attention stays intact.
Second, put the contract in the manifest. An agent should be able to read, before it reads any number, how the service handles confidence, what a null means, what happens when a source is unavailable, and what licence obligations attach to the records. Otherwise the agent will treat an unknown as a zero, and the error will surface three steps downstream where nobody can trace it.
Budgeting time and money
Agents are impatient in a way that shapes server design.
Every tool call costs the caller latency and tokens, and a model with a fixed budget will abandon a promising line of reasoning if a call takes too long. If an endpoint can be slow on a cold path, say so in the description. A runtime that knows a call may take a minute can wait for it. A runtime that expected a second will treat the same call as a failure and try something worse.
The same applies to expense. A tool that triggers paid inference or queues real processing should announce that plainly, both so the model uses it deliberately and so the operator can defend a tighter limit on it. Agents are perfectly capable of calling an expensive endpoint in a loop, and the only durable defence is a limit, not a hope.
Response size matters too. A tool that returns ten thousand records fills the model's context and crowds out its own reasoning. Sample, paginate, or summarise by default, and let the caller ask for more.
Errors, nulls and the things agents do wrong
Autonomous consumers fail differently from humans, and a server should be designed around those specific failure modes.
A human seeing an empty result thinks the query probably missed. A model seeing an empty result frequently concludes there is nothing there. If your response for unsurveyed ground looks identical to your response for genuinely empty ground, agents will confidently report absence you never claimed. Distinguish the two explicitly, with a field rather than a convention.
A human hitting a rate limit waits. A model hitting an unexplained refusal often retries immediately, then reformulates the request, then retries again. Return a structured error with a reason and a retry hint, and the behaviour becomes sane.
A human reading a cold-start delay understands that slow means working. A model with a short timeout treats it as failure and abandons a tool that would have answered. Declare expected latency in the tool description so the runtime can budget for it.
Testing a tool surface
Conventional API tests check that a request produces the right response. Agent-facing tests have to check something harder: that a model presented with a realistic question picks the right tool and fills the parameters correctly.
The practical method is a small evaluation set of questions with known-good tool sequences, run against the manifest whenever a description changes. It catches the failure that unit tests never will, which is that a wording change quietly made one tool more attractive than the tool that should have been chosen.
It also catches drift. Add a fifteenth tool and the model's selection behaviour across the previous fourteen can shift, because you have changed the context it chooses within.
Statelessness is a feature
A stateless MCP server, where each request is self-contained rather than tied to a session, is easier to operate and easier to consume. You can load balance freely, retry without coordination, and cache at the edge. For read-heavy spatial data, there is rarely a reason to hold session state, and holding it costs you all three of those properties.
Statelessness also simplifies the security story. There is no session to hijack, no cursor to leak between callers, and no cleanup path to get wrong. The tradeoff is that genuinely long-running work has to be modelled explicitly, usually as a request that queues and a separate read that reports status, which is a clearer design anyway.
Security and abuse, briefly
Exposing tools to autonomous callers changes the threat model in one specific way: the caller can be persuaded by content it reads. An agent that ingests a web page containing instructions may attempt tool calls it was never asked to make.
The defences are unglamorous. Keep write operations separate from reads and require explicit credentials for them. Make any tool that queues real work, costs money, or mutates state say so in its description and enforce tighter limits on it. Validate parameters server-side rather than trusting the schema was honoured. And prefer tools that cannot do damage when called wrongly, because eventually they will be.
Which door to build first
If you are choosing where to start rather than building both, the question is who your first ten users are.
Build REST first when a human developer will integrate you into a product, when your consumers need to call you from environments with no agent runtime, or when your data is most valuable inside dashboards and reports.
Build MCP first when your value is compositional, when the right sequence of calls depends on intermediate answers, or when your realistic users are already sitting inside an agent runtime and will never write an integration by hand.
Build both when the underlying surface is the same, which for read-only data it almost always is. The cost of the second door is mostly the description writing, and that work improves the first door too.
Where this is going
The current generation of agents reads the internet well and the physical world badly. They can summarize a document about a city and cannot tell you how wide its streets are.
Protocols like MCP are the plumbing that closes that gap, but plumbing is not the hard part. The hard part is that the world data on the other end has to be honest about its own uncertainty, or agents will act on confident nonsense faster than any human ever could. The protocol makes the data reachable. The contract makes it safe to reach for.