MCP primitives: the mental model behind the protocol

If you’ve looked at MCP servers or examples, you’ve probably seen terms like resources, tools, prompts, and roots show up repeatedly.

Those aren’t implementation details. They’re the primitives MCP is built around.

Understanding these primitives makes it easier to design MCP servers, reason about agent behavior, and avoid accidental complexity. This post breaks them down at a conceptual level.

💡
As MCP adoption grows, teams are starting to run into operational challenges around authentication, tool access, and visibility.

That’s why we built the MCP Gateway: a centralized control layer to run MCP-powered agents in production.

Check it out!

What are MCP primitives?

In MCP, primitives are the core object types defined by the protocol.

They describe the kinds of things an MCP server can expose to a client, and the kinds of interactions a client can have with a server. Every MCP interaction is expressed using one or more of these primitives.

MCP defines primitives for:

  • Exposing data and context
  • Exposing executable actions
  • Providing reusable instructions
  • Controlling model generation
  • Defining scope and boundaries

Anything not covered by these primitives is intentionally outside the protocol. MCP does not define agent behavior, workflows, or application logic. Those are left to implementations.

This is what keeps MCP small and consistent across servers, clients, and tools.

Resources

Resources are how an MCP server exposes data and context.

A resource represents something an agent can read or reference. It might be a file, a document, a database record, or some other piece of state managed by the server. The important part is that resources are not executable.

They exist purely to provide context.

This separation is intentional. By keeping resources read-focused, MCP avoids mixing data access with side effects. Agents can inspect, reason over, or summarize resources without triggering actions.

In practice, resources are what give agents grounding. They make context explicit and shared, instead of being embedded inside prompts or hidden in application logic.

Tools

Tools are how an MCP server exposes actions.

A tool represents an operation the server is willing to perform on behalf of a client. Each tool has a clear name, a defined set of inputs, and a defined output. When a tool is invoked, something happens.

This explicitness is the point.

Unlike resources, tools can have side effects. They might query a database, write a file, trigger an API call, or change system state. Because of that, MCP requires tools to be called intentionally, rather than implicitly through prompts or context.

By separating tools from resources, MCP makes it clear when an agent is reasoning versus when it is acting. That clarity becomes important as systems grow and need stronger controls, auditing, or debugging.

MCP uses JSON Schema for validation. Each tool performs a single operation with clearly defined inputs and outputs. Tools may require user consent prior to execution, helping to ensure users maintain control over actions taken by a model.

Prompts

Prompts in MCP are reusable instructions exposed by a server.

Instead of every client or agent defining its own instructions, MCP allows servers to define prompts once and make them available as a primitive. These prompts describe how a model should behave for a given task, without performing any action themselves.

Prompts are not logic and they are not execution. They exist to guide generation in a consistent way.

This is useful when multiple agents need to follow the same instruction pattern, or when prompt quality needs to be controlled centrally. By treating prompts as a primitive, MCP keeps instructions visible and explicit, rather than buried inside application code.

Sampling

Sampling controls how a model generates output.

In MCP, sampling parameters are treated separately from prompts. Things like randomness, output length, or other generation controls are not hidden inside instruction text. They are expressed explicitly.

This separation keeps behavior predictable. The prompt describes what the model should do. Sampling controls how it does it.

Sampling becomes especially important in systems that need consistency, evaluation, or safety guarantees. By making it a primitive, MCP ensures generation behavior can be reasoned about and adjusted without changing the prompt itself.

Roots

Roots define scope in MCP.

A root represents the boundary within which resources, tools, and prompts are exposed. Anything outside that boundary is not visible to the client.

This makes scope explicit. Instead of assuming what an agent can access, MCP requires servers to define it. Resources, tools, and prompts all exist within a root.

Roots are what allow MCP servers to safely expose functionality in larger systems. They support isolation across projects, users, or environments without changing how the rest of the protocol works.

How these primitives work together

MCP primitives are designed to be used together, not in isolation.

A typical interaction looks like this:

  • A root defines what is in scope
  • Resources provide the context an agent can read
  • Prompts shape how the model should reason
  • Sampling controls how the model generates output
  • Tools are called explicitly when action is required

Each step is clear. Context, instruction, behavior, and execution never blur into one another.

This separation is what keeps MCP systems understandable as they grow. When something goes wrong, it’s easier to see whether the issue came from missing context, unclear instructions, generation behavior, or an unintended tool call.

MCP doesn’t prescribe workflows. It provides the primitives that make reliable workflows possible.

Closing thoughts

MCP primitives are intentionally simple.

They define the minimum set of concepts that every MCP server and client needs to agree on. Everything else, agent behavior, orchestration, policy, and governance, is built on top.

Once these primitives are clear, MCP becomes easier to reason about, easier to extend, and easier to operate in real systems.