# Implementing Model Context Protocol (MCP) Servers and Clients from Scratch: A Complete Guide

> Build MCP servers and clients from scratch. Standardize LLM tool discovery and invocation with this complete guide to the Model Context Protocol.

- Repository: [Rohit Ghumare/ai-engineering-from-scratch](https://github.com/rohitg00/ai-engineering-from-scratch)
- Tags: how-to-guide
- Published: 2026-07-26

---

**The Model Context Protocol (MCP) enables any LLM host to discover and invoke external tools, resources, and prompts through a standardized JSON‑RPC 2.0 interface, implemented via three core primitives registered on an `MCPServer` and consumed by an `MCPClient`.**

Implementing Model Context Protocol (MCP) servers and clients from scratch allows you to expose custom capabilities to LLM hosts like Claude Desktop or ChatGPT without vendor‑lock. According to the `rohitg00/ai-engineering-from-scratch` repository, MCP has emerged as the de‑facto standard for local and remote tool integration, using Python type hints to auto‑generate JSON schemas and supporting both stdio and HTTP transports.

## What Is the Model Context Protocol (MCP)?

MCP is an open specification that defines how an LLM **host** (the application) communicates with external **servers** that provide tools, resources, and prompts. In [`phases/11-llm-engineering/14-model-context-protocol/docs/en.md`](https://github.com/rohitg00/ai-engineering-from-scratch/blob/main/phases/11-llm-engineering/14-model-context-protocol/docs/en.md), the protocol is described as a JSON‑RPC 2.0 wire format where:

- **Tools** are executable functions (like `add` or `delete_user`) that perform side effects or calculations.
- **Resources** are read‑only data sources (like `config://app`) that provide context without executing code.
- **Prompts** are reusable templates (like `code_review`) that standardize common interactions.

The server advertises these primitives via JSON schemas derived from Python type hints, and the client drives discovery and invocation through a well‑defined handshake.

## Core Architecture of MCP

### The Three Logical Components

Every MCP deployment consists of three distinct roles:

| Component | Responsibility | Implementation |
|-----------|---------------|----------------|
| **Server** | Registers tools, resources, and prompts; handles JSON‑RPC requests | `MCPServer` class in [`code/main.py`](https://github.com/rohitg00/ai-engineering-from-scratch/blob/main/code/main.py) (lines 48‑58) |
| **Client** | Manages connection, discovery, and invocation; embedded in the host | `MCPClient` class in [`code/main.py`](https://github.com/rohitg00/ai-engineering-from-scratch/blob/main/code/main.py) (lines 31‑44) |
| **Host** | The LLM application that translates model‑generated `tool_use` blocks into `tools/call` RPCs | Orchestrates the client and feeds it tool schemas |

### The Handshake and Discovery Flow

Before invocation, the client and server perform a capability exchange. 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 84‑90), the client sends an `initialize` request containing its protocol version, and the server responds with its name, version, and advertised capabilities.

Following initialization, the host queries the server for available primitives:

1. **`tools/list`** – Returns JSON schemas for every function registered with `@server.tool()`.
2. **`resources/list`** – Returns URI patterns for read‑only data registered with `@server.resource()`.
3. **`prompts/list`** – Returns templates registered with `@server.prompt()`.

The server builds these schemas automatically from Python type hints supplied to the registration decorators (lines 59‑75).

### Invocation and Transport

When the LLM emits a `tool_use` block, the host calls `tools/call` with the tool name and arguments. The server executes the registered handler and returns a JSON‑encoded result (lines 101‑105).

MCP supports two transport mechanisms:

- **Stdio** – A child process that streams JSON‑RPC messages over `stdin/stdout`. Ideal for local development and simple integrations.
- **Streamable HTTP** – A POST‑based HTTP server with optional Server‑Sent Events for progress updates. Recommended for remote SaaS tools and production deployments.

## Building an MCP Server from Scratch

### Minimal Server with FastMCP

For production use, the `FastMCP` class provides a high‑level decorator interface. This example from the lesson docs registers a calculator tool, a configuration resource, and a code review prompt:

```python
from mcp.server.fastmcp import FastMCP

mcp = FastMCP("demo-server")

@mcp.tool()
def add(a: int, b: int) -> int:
    """Add two integers."""
    return a + b

@mcp.resource("config://app")
def app_config() -> str:
    """Return the app's current JSON config."""
    return '{"env": "prod", "region": "us-east-1"}'

@mcp.prompt()
def code_review(language: str, code: str) -> str:
    """Review code for correctness and style."""
    return f"You are a senior {language} reviewer. Review:\n\n{code}"

```

*Source:* [`phases/11-llm-engineering/14-model-context-protocol/docs/en.md`](https://github.com/rohitg00/ai-engineering-from-scratch/blob/main/phases/11-llm-engineering/14-model-context-protocol/docs/en.md) (lines 46‑70)

### In-Process Server and Client

The repository ships a dependency‑free reference implementation in [`code/main.py`](https://github.com/rohitg00/ai-engineering-from-scratch/blob/main/code/main.py) that demonstrates registration, discovery, and invocation without external libraries:

```python

# Build a demo server

server = MCPServer("demo-server")

@server.tool(
    name="add",
    description="Add two integers and return the sum.",
    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}

# In-process client

client = MCPClient(server)
init = client.request("initialize", {"protocolVersion": PROTOCOL_VERSION,
                                    "clientInfo": {"name": "demo-client"}})
tools = client.request("tools/list")["tools"]
add_result = client.request("tools/call",
                            {"name": "add", "arguments": {"a": 40, "b": 2}})
print(add_result["content"][0]["text"])   # → {"sum": 42}

```

*Source:* [`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 53‑66)

### Production Deployment with HTTP Transport

To expose your server over the network, switch from stdio to the streamable HTTP transport:

```python
if __name__ == "__main__":
    mcp.run(transport="streamable-http", host="0.0.0.0", port=8765)

```

*Source:* [`phases/11-llm-engineering/14-model-context-protocol/docs/en.md`](https://github.com/rohitg00/ai-engineering-from-scratch/blob/main/phases/11-llm-engineering/14-model-context-protocol/docs/en.md) (lines 97‑104)

## Safety Patterns and Security Controls

Implementing Model Context Protocol (MCP) servers and clients from scratch requires strict safety controls to prevent malicious exploitation. The `rohitg00/ai-engineering-from-scratch` repository mandates three defensive patterns:

- **Capability allowlists** – Restrict which filesystem or network paths a tool may access, ensuring tools cannot read sensitive files outside their scope.
- **Human‑in‑the‑loop for destructive actions** – Annotate dangerous tools with `destructiveHint: true` to force UI confirmation before execution.
- **Tool‑poisoning defense** – Treat resource content as untrusted data; never inject resources directly into system messages without sanitization.

## Summary

- **MCP standardizes** tool, resource, and prompt exposure via JSON‑RPC 2.0, implemented through the `MCPServer` and `MCPClient` classes in [`code/main.py`](https://github.com/rohitg00/ai-engineering-from-scratch/blob/main/code/main.py).
- **Three primitives** define the protocol: executable **tools**, read‑only **resources**, and template **prompts**, registered via decorators that introspect Python type hints.
- **Two transports** are available: stdio for local development and streamable HTTP for production SaaS deployments.
- **Security is mandatory**—implement capability allowlists, destructive action confirmations, and resource sanitization to prevent tool poisoning.

## Frequently Asked Questions

### What is the Model Context Protocol (MCP) used for?

MCP provides a universal interface for LLM hosts to discover and invoke external capabilities. It allows developers to expose local scripts, database queries, or API integrations as standardized tools that any compatible AI assistant can use, eliminating the need for custom plugins per platform.

### How does MCP differ from traditional REST API integrations?

Unlike REST APIs that require manual schema documentation and HTTP client configuration, MCP uses JSON‑RPC 2.0 with automatic schema generation from Python type hints. The protocol includes built‑in discovery mechanisms (`tools/list`, `resources/list`) and supports bidirectional streaming via stdio or HTTP, whereas REST typically operates over HTTP only with static OpenAPI specs.

### What transport protocols does MCP support?

MCP supports two primary transports: **stdio** for local subprocess communication (ideal for desktop applications like Claude Desktop) and **streamable HTTP** for remote network access (suitable for cloud‑hosted services). The transport is configurable via the `run()` method in the FastMCP server implementation.

### How do I secure an MCP server against malicious tool calls?

Secure your server by implementing **capability allowlists** to restrict filesystem and network access, marking destructive operations with `destructiveHint` to trigger human confirmation, and sanitizing all resource content before injection into prompts. These patterns are documented in [`phases/11-llm-engineering/14-model-context-protocol/docs/en.md`](https://github.com/rohitg00/ai-engineering-from-scratch/blob/main/phases/11-llm-engineering/14-model-context-protocol/docs/en.md) and are essential for production deployments.