Almost every MCP server I have looked at exposes everything as a tool.

It is the path of least resistance, and it silently moves control from the person to the model.

Three primitives, three different people in charge

PrimitiveWho invokes itTypical use
ToolThe modelActions with side effects
ResourceThe host applicationRead-only context
PromptThe user, deliberatelyReusable workflow templates

That middle column is the whole point. It says who decides a capability runs, rather than describing what it does.

Expose a customer record lookup as a tool and the model can pull records whenever it judges that useful. Expose it as a resource and your application decides what enters context. The underlying query is identical. The control model is not.

The same applies to prompts. A workflow template offered as a prompt is something a user picks knowingly. The same template buried in a tool description becomes invisible product behaviour that nobody reviewed.

Schemas are the actual guardrail

Once something is a tool, its schema is the only thing standing between a plausible-sounding model argument and a real side effect.

Broad schemas invite guessing. A query field typed as a free string will eventually receive something nobody designed for. Narrow, typed, server-validated inputs turn that into a clean rejection rather than an incident.

Two habits that pay for themselves:

  • Validate on the server, always. Client-side or prompt-level validation is a suggestion, not a control.
  • Return structured errors the model can act on. "Invalid date range, expected ISO 8601" produces a correction. "Error" produces a retry loop.

And return enough metadata with results to make review possible later: source, timestamp, status. Without it you cannot reconstruct why the system did what it did, and every evaluation you try to run afterwards is guesswork.

Pick the primitive before you name the capability. It is a permissions decision wearing the costume of an API design choice.

Part 3 of 5 Days of MCP for Production AI.