Menu

AI Agents course · Lesson 3 of 7

MCP (Model Context Protocol) Explained Simply, with Code

What the Model Context Protocol (MCP) is, how MCP servers and clients talk, what tools, resources and prompts are, and how to use it safely, with runnable code.

  • Intermediate
  • 5 min read
  • Updated Oct 2026
On this page
  1. The three roles
  2. What a server offers
  3. The messages
  4. MCP and RAG
  5. Security rules
  6. Common mistakes
  7. Key takeaway

Every AI application needs to reach other systems: databases, file stores, ticketing tools, code repositories. Without a standard, each application writes its own integration for each system. The Model Context Protocol (MCP) is an open standard that fixes this. A system is wrapped once as an MCP server, and any MCP-compatible application can discover and use it.

A common comparison is a universal plug: instead of a different cable for every device, one standard connector works everywhere.

The three roles

  • Host: the AI application the user works with, such as a chat assistant, an IDE or an agent.
  • Client: the part of the host that keeps a connection to one server.
  • Server: a program that exposes a system’s capabilities in the MCP format.

Servers can run locally (connected through standard input and output) or remotely over HTTP.

What a server offers

  • Tools: actions the model can ask to run, each with a name, description and input schema. For example get_order_status.
  • Resources: data the application can read, such as files or database records, identified by URIs.
  • Prompts: reusable prompt templates the user can pick.

The model uses tools exactly as in tool calling; MCP standardises how the application finds and calls them.

The messages

MCP uses JSON-RPC 2.0: each request has a method name, parameters and an ID, and each response has a result or an error with the same ID. The core sequence is: initialize (agree on a protocol version and capabilities), tools/list (discover the tools), then tools/call (run one).

This example shows those messages with a tiny in-process server and client. It is a simplified illustration of the message shapes, not a full implementation:

import json

# --- A tiny MCP-style server: one system (an order database) exposed as tools -------------------
ORDERS = {"A-1042": "shipped", "B-2210": "delivered"}

TOOLS = [{
    "name": "get_order_status",
    "description": "Return the delivery status of an order.",
    "inputSchema": {"type": "object", "properties": {"order_id": {"type": "string"}}, "required": ["order_id"]},
}]

def handle(request):
    """Answer one JSON-RPC 2.0 request, the message format MCP uses."""
    method, rid = request["method"], request.get("id")
    if method == "initialize":
        result = {"protocolVersion": request["params"]["protocolVersion"],
                  "capabilities": {"tools": {}}, "serverInfo": {"name": "orders-server", "version": "0.1"}}
    elif method == "tools/list":
        result = {"tools": TOOLS}
    elif method == "tools/call":
        args = request["params"]["arguments"]
        status = ORDERS.get(args.get("order_id"))
        text = f"Order {args.get('order_id')} is {status}." if status else "Order not found."
        result = {"content": [{"type": "text", "text": text}], "isError": status is None}
    else:
        return {"jsonrpc": "2.0", "id": rid, "error": {"code": -32601, "message": "Method not found"}}
    return {"jsonrpc": "2.0", "id": rid, "result": result}

# --- A client (the AI application) talks to it with the same messages ------------------------
def send(method, params, rid):
    request = {"jsonrpc": "2.0", "id": rid, "method": method, "params": params}
    response = handle(json.loads(json.dumps(request)))      # over stdio or HTTP in a real setup
    return response["result"] if "result" in response else response["error"]

info = send("initialize", {"protocolVersion": "2025-06-18", "capabilities": {},
                           "clientInfo": {"name": "demo-client", "version": "0.1"}}, 1)
print("connected to:", info["serverInfo"]["name"])
tools = send("tools/list", {}, 2)["tools"]
print("tools offered:", [t["name"] for t in tools])
print("call:", send("tools/call", {"name": "get_order_status", "arguments": {"order_id": "A-1042"}}, 3))
print("call:", send("tools/call", {"name": "get_order_status", "arguments": {"order_id": "Z-0000"}}, 4))
connected to: orders-server
tools offered: ['get_order_status']
call: {'content': [{'type': 'text', 'text': 'Order A-1042 is shipped.'}], 'isError': False}
call: {'content': [{'type': 'text', 'text': 'Order not found.'}], 'isError': True}

The client discovered the tool at run time instead of having it built in, and tool errors come back as results the model can read and react to. For real servers, use an official MCP SDK, which handles the transport, the handshake details and the current protocol version.

MCP and RAG

MCP does not replace retrieval. A RAG search service can be exposed as an MCP tool, so an agent calls it when it needs documents. RAG decides what knowledge reaches the model; MCP decides how the model reaches systems. See RAG vs MCP.

Security rules

Connecting an MCP server gives the model new powers, so treat it like installing software with access to your data:

  1. Only connect servers you trust, and review what each tool can do.
  2. Least privilege: give each server the narrowest access it needs, using the real user’s identity.
  3. Require confirmation for tools that change data, spend money or send messages.
  4. Treat tool results as untrusted: text returned by a server can contain instructions aimed at the model (prompt injection).
  5. Log every tool call with its arguments and results.

Common mistakes

  1. Assuming MCP makes tools safe. It standardises the connection; permissions are still your job.
  2. Exposing too many tools at once, which confuses the model and widens the attack surface.
  3. Hard-coding an old protocol version instead of using a maintained SDK.

Key takeaway

MCP is an open standard that lets any compatible AI application discover and use tools, resources and prompts from MCP servers, using JSON-RPC messages such as initialize, tools/list and tools/call. Write each integration once and reuse it everywhere, but keep permissions, confirmations and logging firmly in your control. Next: evaluating and securing agents.

By , Data Engineer and Founder of DataDank · Last reviewed Oct 2026 · MCP is an evolving open standard; this lesson describes its core ideas. The example is a simplified in-process illustration of the message shapes, not a complete implementation; for real servers use an official SDK and check the current specification and protocol version.

Progress is saved in this browser only. No account needed.

Search
Filter by type