PREDICTIVE TREND INSIGHT
How to build a Model Context Protocol plugin from scratch Illustration

How to build a Model Context Protocol plugin from scratch

Direct Summary:

An MCP server exposes three distinct kinds of capability to a client, not just one: tools (functions the model can call, like a calculator or a web fetch), resources (read-only data the client can attach as context, like a file or a database record), and prompts (reusable, user-triggered message templates, often surfaced as slash commands). Building a server "from scratch" means deciding which of these three primitives actually fits what you're exposing — a common mistake is wrapping everything as a tool when a resource or prompt is the better fit.

"Simplicity is the ultimate sophistication."

— Leonardo da Vinci

Key Insights

  • Three primitives, not one: Tools (model-invoked functions), resources (client-attached read data), and prompts (user-triggered templates) each solve a different problem.
  • Resources use URIs: A resource is addressed like greeting://{name}, not called with arguments the way a tool is.
  • Prompts are user-controlled: The spec explicitly designs prompts to be explicitly selected by the user (often as a slash command), not silently invoked by the model.

The local-setup lesson covered spinning up a minimal server with one tool. Building a real MCP server from scratch means understanding all three capability types the protocol defines — because picking the wrong one for a given piece of functionality is a common design mistake.

Tools vs. resources vs. prompts

Tools are functions the model decides to call on its own, based on the conversation — a calculator, a database query, a file write. Resources are read-only data the client (or user) attaches to the conversation as context — think of them as addressable documents, not actions; a resource is identified by a URI template like greeting://{name} rather than invoked with a function call. Prompts are reusable message templates that the protocol spec explicitly designs to be user-triggered — for example, exposed as a slash command in a chat UI — rather than something the model decides to invoke by itself.

Using Python's official FastMCP SDK, each primitive gets its own decorator:

server.py
from mcp.server.fastmcp import FastMCP

mcp = FastMCP("Docs Assistant")

# TOOL — the model calls this itself, with arguments
@mcp.tool()
def add(a: int, b: int) -> int:
    """Add two numbers."""
    return a + b

# RESOURCE — addressable data, not an action
@mcp.resource("greeting://{name}")
def greeting(name: str) -> str:
    """Greet someone by name."""
    return f"Hello, {name}!"

if __name__ == "__main__":
    mcp.run()

Under the hood, each primitive maps to its own pair of JSON-RPC methods — a client calls tools/list then tools/call for tools, and the equivalent prompts/list / prompts/get for prompts. A prompt's response isn't free text — it returns one or more structured PromptMessage objects, each with a role ("user" or "assistant") and typed content (text, image, audio, or an embedded resource), which the client then feeds into the conversation.

Primitive Who invokes it Typical use
Tool The model, autonomously Actions with side effects: fetch a URL, run a calculation, write a file
Resource The client or user, explicitly Read-only context: a file's contents, a config value, a database record
Prompt The user, explicitly (e.g. a slash command) Reusable templates: "review this code," "summarize this ticket"

Getting this split right matters for more than tidiness: because tools are the one primitive the model can trigger on its own, the spec's security guidance is strictest there — a host application must get explicit user consent before invoking any tool, since tool descriptions represent arbitrary code execution and should be treated as untrusted unless they come from a server you already trust.

Practical Challenge

Extend the server above with a @mcp.resource("file://{path}") that reads a local file's contents, and a @mcp.prompt() that generates a "summarize this file" message template referencing that resource.

Concept Check

Which MCP primitive is designed to be explicitly selected by the user (e.g. as a slash command), rather than invoked automatically by the model?
Correct! The MCP spec defines prompts as user-controlled — servers expose them with the intent that a person picks them explicitly, unlike tools, which the model can call on its own.
Incorrect. Try again! Hint: Tools are model-invoked; prompts are the primitive the spec designs specifically for explicit user selection.

Sources & Further Reading

Previous Guide Dashboard Next Guide