# Implementation Details of the Model Context Protocol (MCP) in AI Lessons: A Complete Technical Guide

> Discover the implementation details of the Model Context Protocol MCP in AI lessons. This guide explains how the JSON-RPC 2.0 server exposes tools, resources, and prompts for LLMs.

- Repository: [Rohit Ghumare/ai-engineering-from-scratch](https://github.com/rohitg00/ai-engineering-from-scratch)
- Tags: deep-dive
- Published: 2026-06-24

---

**The Model Context Protocol (MCP) implementation in the AI-Engineering-From-Scratch curriculum provides a lightweight JSON-RPC 2.0 server that exposes three primitives—Tools, Resources, and Prompts—enabling LLMs to discover and invoke capabilities through a standardized contract.**

This deep dive explores the reference MCP implementation found in the `ai-engineering-from-scratch` repository, specifically within the Phase 11 LLM engineering lessons. Understanding these implementation details provides the foundation for building production-grade AI systems that separate model reasoning from tool execution.

## MCP Protocol Overview and Core Primitives

The Model Context Protocol defines a client-server architecture where an LLM (client) communicates with a server offering three distinct service primitives. According to the source code in [`phases/11-llm-engineering/14-model-context-protocol/code/main.py`](https://github.com/rohitg00/ai-engineering-from-scratch/blob/main/phases/11-llm-engineering/14-model-context-protocol/code/main.py), these primitives are:

- **Tools**: Pure functions that perform computation or side effects, exposed via `tools/list` and `tools/call` methods
- **Resources**: Read-only data blobs accessible through `resources/list` and `resources/read` methods  
- **Prompts**: Template-driven messages that the LLM can render before processing, available via `prompts/list` and `prompts/get`

Each primitive follows a JSON-RPC 2.0 request-response pattern, with the protocol version pinned to `"2025-06-18"` in the reference implementation.

## Core Architecture and Data Structures

The implementation uses Python dataclasses to define the three core entities, separating static metadata from executable handlers.

### Protocol Version and Entity Definitions

The server declares its protocol compliance at the module level:

```python
PROTOCOL_VERSION = "2025-06-18"

```

The three entity dataclasses defined in [`phases/11-llm-engineering/14-model-context-protocol/code/main.py`](https://github.com/rohitg00/ai-engineering-from-scratch/blob/main/phases/11-llm-engineering/14-model-context-protocol/code/main.py) (lines 24-46) encapsulate both metadata and callable implementations:

```python
@dataclass
class Tool:
    name: str
    description: str
    input_schema: dict[str, Any]
    handler: Callable[..., Any]
    destructive: bool = False

@dataclass
class Resource:
    uri: str
    description: str
    handler: Callable[[], str]

@dataclass
class Prompt:
    name: str
    description: str
    arguments: list[str]
    handler: Callable[..., str]

```

These structures enable type-safe registration while maintaining the flexibility to attach arbitrary callable logic.

## Server Implementation and Registration

The `MCPServer` class manages three internal dictionaries (`tools`, `resources`, `prompts`) and provides decorator-based registration helpers that follow the curriculum's declarative pattern.

### Decorator-Based Registration Pattern

The `tool` decorator (lines 59-63) captures function metadata and stores the implementation:

```python
def tool(self, name: str, description: str, schema: dict[str, Any], *, destructive: bool = False):
    def decorator(fn):
        self.tools[name] = Tool(name, description, schema, fn, destructive)
        return fn
    return decorator

```

Equivalent registration helpers exist for resources (`resource`, lines 65-71) and prompts (`prompt`, lines 71-75). This pattern allows lesson authors to expose capabilities without boilerplate:

```python
@server.tool(
    name="translate",
    description="Translate English text to French.",
    schema={"type": "object",
            "properties": {"text": {"type": "string"}},
            "required": ["text"]},
)
def translate(text: str) -> dict[str, str]:
    return {"translation": f"[FAKE FR] {text}"}

```

### JSON-RPC Dispatch Logic

The `handle` method (lines 77-124) serves as the central request router, extracting `method`, `params`, and `id` from incoming JSON-RPC messages:

```python
def handle(self, message: dict[str, Any]) -> dict[str, Any]:
    method = message.get("method")
    params = message.get("params") or {}
    request_id = message.get("id")
    
    if method == "initialize":
        result = {"protocolVersion": PROTOCOL_VERSION, "capabilities": {}}
    elif method == "tools/list":
        result = {"tools": [t.__dict__ for t in self.tools.values()]}
    elif method == "tools/call":
        tool = self.tools[params["name"]]
        output = tool.handler(**params.get("arguments", {}))
        result = {"content": [{"type": "text", "text": json.dumps(output)}]}
    # ... additional method routing

    else:
        return {"jsonrpc": "2.0", "id": request_id,
                "error": {"code": -32601, "message": f"unknown method: {method}"}}
    return {"jsonrpc": "2.0", "id": request_id, "result": result}

```

Key implementation details include:

- **Protocol Handshake**: The `initialize` method returns the protocol version and server capabilities
- **Destructive Operation Hints**: Tools marked with `destructive=True` trigger the addition of `annotations.destructiveHint: true` in the response (lines 96-98), allowing clients to warn users before execution
- **Standardized Error Codes**: Missing parameters raise `-32602` (Invalid Params), while unknown methods return `-32601` (Method Not Found), adhering strictly to the JSON-RPC 2.0 specification

## Client-Side Communication

The `MCPClient` class provides an in-process simulation of a real remote client, composing JSON-RPC requests and unwrapping responses:

```python
def request(self, method: str, params: dict[str, Any] | None = None) -> Any:
    message = {"jsonrpc": "2.0", "id": self._next_id(),
               "method": method, "params": params or {}}
    response = self.server.handle(message)
    if "error" in response:
        raise RuntimeError(response["error"]["message"])
    return response["result"]

```

The client maintains monotonically increasing request IDs via `_next_id()` to satisfy JSON-RPC requirements for unique identifiers.

## Complete Working Example

The reference implementation demonstrates a full lifecycle in [`main.py`](https://github.com/rohitg00/ai-engineering-from-scratch/blob/main/main.py), starting with server instantiation and tool registration:

```python
server = MCPServer("demo-server")

@server.tool(name="add", description="Add two integers", schema={"type": "object", "properties": {"a": {"type": "integer"}, "b": {"type": "integer"}}, "required": ["a", "b"]})
def add(a: int, b: int) -> dict[str, int]:
    return {"sum": a + b}

@server.resource("config://app", "Application configuration")
def get_config() -> str:
    return json.dumps({"debug": True, "version": "1.0.0"})

```

The client interaction pattern follows a strict discovery-then-execution flow:

```python
client = MCPClient(server)

# Protocol initialization

init = client.request("initialize", {"protocolVersion": PROTOCOL_VERSION})

# Capability discovery

tools = client.request("tools/list")["tools"]

# Tool invocation

result = client.request("tools/call", {"name": "add", "arguments": {"a": 40, "b": 2}})

# Returns: {"content": [{"type": "text", "text": "{\"sum\": 42}"}]}

# Resource access

config = client.request("resources/read", {"uri": "config://app"})

```

## Integration with the Broader Curriculum

This foundational implementation in [`phases/11-llm-engineering/14-model-context-protocol/code/main.py`](https://github.com/rohitg00/ai-engineering-from-scratch/blob/main/phases/11-llm-engineering/14-model-context-protocol/code/main.py) serves as the building block for advanced lessons in the repository:

- **Phase 13 "Tools & Protocols"**: Extends the basic server with a **registry** and **OPA-based policy gate** ([`phases/19-capstone-projects/13-mcp-server-with-registry/code/main.py`](https://github.com/rohitg00/ai-engineering-from-scratch/blob/main/phases/19-capstone-projects/13-mcp-server-with-registry/code/main.py))
- **Phase 13 Lesson 18**: Implements production-grade **MCP authentication** with JWT signing and JWKS caching ([`phases/13-tools-and-protocols/18-mcp-auth-production/code/main.py`](https://github.com/rohitg00/ai-engineering-from-scratch/blob/main/phases/13-tools-and-protocols/18-mcp-auth-production/code/main.py))
- **Phase 16 "Multi-Agent & Swarms"**: Positions MCP as the vertical agent-to-tool channel, contrasting with A2A (Agent-to-Agent) for horizontal communication between agents

The progression from the in-process demo to stdio-based servers ([`phases/13-tools-and-protocols/07-building-an-mcp-server/code/main.py`](https://github.com/rohitg00/ai-engineering-from-scratch/blob/main/phases/13-tools-and-protocols/07-building-an-mcp-server/code/main.py)) and multi-server clients ([`phases/13-tools-and-protocols/08-building-an-mcp-client/code/main.py`](https://github.com/rohitg00/ai-engineering-from-scratch/blob/main/phases/13-tools-and-protocols/08-building-an-mcp-client/code/main.py)) illustrates the protocol's transport flexibility.

## Summary

- **The Model Context Protocol** is implemented as a JSON-RPC 2.0 server in [`phases/11-llm-engineering/14-model-context-protocol/code/main.py`](https://github.com/rohitg00/ai-engineering-from-scratch/blob/main/phases/11-llm-engineering/14-model-context-protocol/code/main.py), supporting three primitives: Tools, Resources, and Prompts
- **Registration architecture** uses Python decorators to bind callable handlers to metadata schemas, with special support for destructive operation annotations
- **Request dispatch** follows strict JSON-RPC semantics, returning standard error codes (`-32601`, `-32602`) for invalid methods or parameters
- **Client implementation** handles message ID generation and response unwrapping, simulating real-world transport over stdio or HTTP
- **Curriculum progression** moves from this basic in-process demo to authenticated, registry-based production systems in later phases

## Frequently Asked Questions

### What distinguishes Tools from Resources in the MCP implementation?

**Tools** are executable functions that perform computation or side effects and accept input parameters defined by JSON schemas, while **Resources** are read-only data blobs identified by URIs that return static content without arguments. According to the source code, tools are invoked via `tools/call` with an `arguments` payload, whereas resources are accessed via `resources/read` using only a URI identifier.

### How does the MCP server handle potentially dangerous operations?

The implementation includes a `destructive` boolean flag in the `Tool` dataclass. When set to `True` during registration, the server adds `annotations.destructiveHint: true` to the tool's metadata in the `tools/list` response. This allows MCP clients to surface warnings to users before executing operations that might modify state or delete data.

### What transport mechanisms does this MCP implementation support?

The reference implementation in Phase 11 uses **in-process** communication by directly calling `server.handle()` from the client. However, the curriculum evolves to support **stdio** transport in [`phases/13-tools-and-protocols/07-building-an-mcp-server/code/main.py`](https://github.com/rohitg00/ai-engineering-from-scratch/blob/main/phases/13-tools-and-protocols/07-building-an-mcp-server/code/main.py), and production deployments typically extend this to HTTP or WebSocket transports while maintaining the same JSON-RPC message format.

### How does the basic MCP server evolve into a production system?

Later lessons in the repository add three critical layers: a **registry** for managing multiple MCP server instances ([`phases/19-capstone-projects/13-mcp-server-with-registry/code/main.py`](https://github.com/rohitg00/ai-engineering-from-scratch/blob/main/phases/19-capstone-projects/13-mcp-server-with-registry/code/main.py)), **OPA-based policy gates** for authorization, and **JWT-based authentication** with JWKS caching ([`phases/13-tools-and-protocols/18-mcp-auth-production/code/main.py`](https://github.com/rohitg00/ai-engineering-from-scratch/blob/main/phases/13-tools-and-protocols/18-mcp-auth-production/code/main.py)). These additions transform the simple JSON-RPC handler into a secure, multi-tenant service mesh component.