MCP Servers, Explained Like You're Going to Build One

The Model Context Protocol standardizes how models discover and call tools. Here's the mental model, the three primitives, and a server in forty lines.

Pooja Thirupuranthakam · Jul 2, 2026 2 min read 8

Before MCP, every AI app wrote its own glue for every integration. Your agent wants GitHub? Write a wrapper. Slack? Another wrapper. Switch to a different AI client? Rewrite all of them. The Model Context Protocol fixes this with a simple idea: integrations are servers, AI apps are clients, and they speak a shared protocol.

The three primitives

  • Tools: functions the model can call. Each has a name, a description, and a JSON schema for arguments. create_issue(repo, title, body).
  • Resources: things the model can read. Files, database rows, URLs. Addressed by URI, returned as text or binary.
  • Prompts: reusable prompt templates the server offers, with arguments. "Summarize this PR" as a first-class thing.

A client connects, asks the server what it offers (tools/list, resources/list), and the model picks from that catalog.

Transport

Two options. stdio: the client spawns the server as a subprocess and talks over stdin/stdout. Perfect for local tools. Streamable HTTP: the server is a web service; good for shared or remote servers. Same protocol either way, JSON-RPC underneath.

A server in Python

from mcp.server.fastmcp import FastMCP

mcp = FastMCP("notes")
NOTES: dict[str, str] = {}

@mcp.tool()
def add_note(title: str, body: str) -> str:
    """Save a note. Returns the note title."""
    NOTES[title] = body
    return title

@mcp.tool()
def search_notes(query: str) -> list[str]:
    """Titles of notes whose body mentions the query."""
    return [t for t, b in NOTES.items() if query.lower() in b.lower()]

@mcp.resource("note://{title}")
def get_note(title: str) -> str:
    return NOTES.get(title, "")

if __name__ == "__main__":
    mcp.run()  # stdio by default

Point any MCP client at it and the model can now take and search notes.

Things people get wrong

  • Tool descriptions are prompts. They're the only thing the model sees. "Search notes" is useless; "Titles of notes whose body mentions the query; case-insensitive substring match" lets the model use it correctly.
  • Too many tools. A server exposing 80 tools blows up the context window. Group, or let clients load tool definitions lazily.
  • Trusting tool output. A resource can contain text that looks like instructions ("ignore previous rules and…"). Treat everything a tool returns as data. See harness engineering.
  • No auth story. Remote servers need OAuth or tokens. Don't ship a server that will happily delete production because a model asked.

Why it won

Not because the protocol is clever (it's deliberately boring) but because it turned an N×M integration problem into N+M. Build a server once; every client gets it.

Written by

Pooja Thirupuranthakam

Senior in Artificial Intelligence at Purdue. I write about how modern AI systems actually get built.

Discussion (0)

  • No comments yet. Start the thread.

Log in to join the discussion.