Français
Applied AI · Advanced 🔴 · Session 5
📝 Teacher's Guide
← Return to program 📄 Source .md

Applied AI — Advanced Level

Session 5: MCP in depth

Program : Applied AI — Professional training in artificial intelligence Instructor: Yann Isola Level : Advanced — Solutions architects preparing for certification Claude Certified Architect Recommended duration: 3.5 hours (2 hours of lecture + 1.5 hours of practical exercises) Prerequisites: Sessions 1 to 4 of the advanced level (orchestration, tools/function calling, context management, multi-agent architecture)


Educational objectives

At the end of this session, participants will be able to:

  1. Describe the complete architecture of the MCP protocol (Model Context Protocol — model context protocol): Host ↔ Client ↔ Server, and the exact role of each component.
  2. Choose and justify a transport layer (local stdio vs remote HTTP Streamable) according to architectural, security and deployment criteria.
  3. Distinguish the three MCP primitives — Tools, Resources, Prompts — and select the correct primitive according to who controls the summoning (the model, application or user). This is a central examination point for certification.
  4. Build a complete MCP server in Python (official SDK) exposing tools, resources and prompts, with integrated safeguards.
  5. Build an MCP client: session management, tool discovery, capacity negotiation.
  6. Apply advanced patterns: dynamic tool registration, resource subscription with notifications, prompt chaining, normalization of heterogeneous data.

Session plan

Block Duration Content
1 30 mins MCP Architecture: Host, Client, Server, JSON-RPC
2 25 mins Transport layer: stdio vs Streamable HTTP
3 35 mins The three primitives: Tools, Resources, Prompts
4 30 mins Build an MCP server and client (Python SDK)
5 20 mins Security, ecosystem and advanced patterns
6 90 mins Practical exercises + interactive demonstration (web page)
7 10 mins Validation quiz and certification summary

Block 1 — MCP Architecture: Host, Client, Server

1.1 The problem that MCP solves

Before MCP, each AI application editor had to write a specific connector for each external tool: N applications × M tools = N×M integrations. This is the classic “M×N” problem that standards solve (like USB-C did for connectivity, or LSP — Language Server Protocol , language server protocol — for code editors).

MCP transforms M×N into M+N : each tool exposes a single standard interface (an MCP server), each application implements a single standard interface (an MCP client). Any server works with any compatible host.

Educational point: the USB-C analogy is officially used by Anthropic. Participants will find it in the certification documentation. Have a participant rephrase it: “MCP is to AI what USB-C is to hardware: a universal port. »

1.2 The three components

┌─────────────────────────── HÔTE ───────────────────────────┐
│  (IDE, application de chat, agent autonome)                 │
│                                                             │
│   ┌──────────┐      ┌──────────┐      ┌──────────┐          │
│   │ Client 1 │      │ Client 2 │      │ Client 3 │          │
│   └────┬─────┘      └────┬─────┘      └────┬─────┘          │
└────────┼─────────────────┼─────────────────┼────────────────┘
         │ JSON-RPC        │ JSON-RPC        │ JSON-RPC
    ┌────▼─────┐      ┌────▼─────┐      ┌────▼─────┐
    │ Serveur  │      │ Serveur  │      │ Serveur  │
    │filesystem│      │  GitHub  │      │PostgreSQL│
    └──────────┘      └──────────┘      └──────────┘
Component Role Examples
Host (Host ) The application that embeds the model and controls the user experience. Decides which servers to connect, enforces security and consent policies. Claude Desktop, an IDE (Cursor, VS Code), a business application
Customer (Customer ) Component managed by the SDK, one client per server (strict 1:1 relationship). Maintains session, performs capacity negotiation, routes messages. Instantiated by the MCP SDK in the host
Server (Server ) Capability Provider: Exposes Tools, Resources, and Prompts via the protocol. Independent process. Filesystem server, GitHub server, home server

Rule to hammer for certification: the Client:Server relationship is 1:1. A host connecting to 3 servers instantiates 3 clients. This isolation is a deliberate architectural choice (see Block 5: security).

1.3 JSON-RPC 2.0: the common language

All MCP messages are messages JSON-RPC 2.0 (Remote Procedure Call — remote procedure call in JSON). Three types of messages:

  1. Request (Request ) — waits for a response, wears a id :
{
  "jsonrpc": "2.0",
  "id": 42,
  "method": "tools/call",
  "params": {
    "name": "rechercher_commande",
    "arguments": { "numero": "CMD-2026-0193" }
  }
}
  1. Answer (Response ) — wears the same id , contains result Or error :
{
  "jsonrpc": "2.0",
  "id": 42,
  "result": {
    "content": [
      { "type": "text", "text": "{\"statut\": \"expédiée\", \"montant\": 129.90}" }
    ]
  }
}
  1. Notification (Notification ) — no id , no response expected:
{
  "jsonrpc": "2.0",
  "method": "notifications/resources/updated",
  "params": { "uri": "db://commandes/CMD-2026-0193" }
}

1.4 The life cycle of a session

  1. initialize : the client sends its protocol version and its abilities (what it knows how to manage: sampling, notifications, etc.). The server responds with its own capabilities (tools, resources, prompts, subscriptions, etc.).
  2. notifications/initialized : the client confirms — the session is open.
  3. Discovery : tools/list , resources/list , prompts/list .
  4. Operations : tools/call , resources/read , prompts/get , subscriptions…
  5. Closing : clean termination of the transport.

Certification point — capacity negotiation: the customer declares what he supports handshake (initial handshake). A server should never send a subscription notification to a client that has not declared the corresponding capacity. On the exam, you will be asked why negotiation exists: to allow the evolution of the protocol without breaking compatibility — an old client and a recent server can cooperate on the intersection of their capabilities.


Block 2 — Transport layer: stdio vs Streamable HTTP

2.1 stdio: local transport

The server is launched as subprocess of the host. JSON-RPC messages flow over stdin/stdout (standard input/output), one JSON message per line.

Features :

Typical configuration (Claude Desktop):

{
  "mcpServers": {
    "commandes": {
      "command": "python",
      "args": ["/opt/mcp/serveur_commandes.py"],
      "env": { "DB_URL": "postgresql://localhost/boutique" }
    }
  }
}

2.2 Streamable HTTP: remote transport

For remote servers (shared, multi-user, cloud), MCP uses HTTP streamable , Who replaces the old SSE transport (Server-Sent Events — events sent by the server), deprecated.

Functioning :

Features :

2.3 Decision matrix (to know for the exam)

Criteria stdio HTTP streamable
Location Same machine as host Remote/cloud machine
Number of users 1 (the local user) N (shared)
Authentication Legacy from the operating system OAuth 2.1 / tokens
Latency Minimal Network (variable)
Deployment Distributed with the host application Operated as a web service
Access to local resources (files, devices) Direct Impossible (or via tunnel)
Typical use case Filesystem server, local development tools Enterprise SaaS server, shared business API

Certification pocket ruler: “Local and single-user files → stdio. Shared, authenticated, scalable service → Streamable HTTP. SSE only → false answer (deprecated). »


Block 3 — The three primitives: Tools, Resources, Prompts

It is the heart of the session and the certification . The question is not “what does the primitive do?” " but " who decides on its invocation? ».

3.1 Control table (to be memorized)

Primitive Who controls? Trigger Analogy
Tool (tool) The model (model-controlled ) The LLM decides to call the tool during its reasoning The agent's hands
Resource (resource) The app (application-controlled ) The host decides what data to inject into the context The Agent's Eyes — read-only
Prompt (guest) The user (user-controlled ) The human explicitly chooses a prompt template (menu, slash command) A pre-filled form

3.2 Tools: invoked by the model

{
  "name": "rembourser_commande",
  "description": "Rembourse une commande. Refusé au-delà de 500 $ sans validation humaine.",
  "inputSchema": {
    "type": "object",
    "properties": {
      "numero": { "type": "string", "description": "Numéro de commande, ex. CMD-2026-0193" },
      "montant": { "type": "number", "description": "Montant en dollars US" }
    },
    "required": ["numero", "montant"]
  }
}

3.3 Resources: exposed by the application

Why “application-controlled”? Because it is the host — not the model — that decides which resources to load in the context. This protects the context window (the host filters) and privacy (the model does not "snoop" freely).

3.4 Prompts: triggered by the user

{
  "name": "rapport_incident",
  "description": "Génère un rapport d'incident structuré",
  "arguments": [
    { "name": "severite", "description": "P1 à P4", "required": true },
    { "name": "systeme", "description": "Système affecté", "required": true }
  ]
}

3.5 Decision tree (reproduced in the interactive web page)

  1. Does the action modify a state or trigger a side effect?Tool .
  2. Is it data to be read, and it is the application that must decide when to inject it?Resource .
  3. Is it a workflow that the human explicitly triggers with parameters?Prompt .
  4. The model must decide alone, through reasoning, to seek out the data? → then even a reading can be a Tool (ex. rechercher_client ). The Resource/Tool boundary passes through who controls , not by read/write alone.

Exam Pitfall #1: “a search in a database is a reading, therefore it is a Resource” — fake if it is the model which must decide to trigger it dynamically. A search invoked by the model is a Tool. Data pre-selected by the application is a Resource.


Block 4 — Build an MCP Server and Client (Python SDK)

4.1 Complete server with guardrails

The official Python SDK (mcp , with the API FastMCP ) provides three decorators. Complete example — order management server with reimbursement safeguard And normalization of dates :

# serveur_commandes.py
# ⚠ Vérifiez la version du SDK : l'API évolue rapidement.
from mcp.server.fastmcp import FastMCP
from datetime import datetime

mcp = FastMCP("commandes")

MAX_REMBOURSEMENT = 500.0  # Garde-fou métier : au-delà, validation humaine

def normaliser_date(valeur: str) -> str:
    """Normalise les dates hétérogènes des systèmes sources vers ISO 8601.
    Les serveurs MCP tiers renvoient des formats variés : c'est au point
    d'intégration de normaliser, jamais au modèle de deviner."""
    formats = ("%Y-%m-%d", "%d/%m/%Y", "%m-%d-%Y", "%d %b %Y", "%Y-%m-%dT%H:%M:%S")
    for fmt in formats:
        try:
            return datetime.strptime(valeur.strip(), fmt).date().isoformat()
        except ValueError:
            continue
    raise ValueError(f"Format de date non reconnu : {valeur!r}")

# ── TOOL : invoqué par le modèle, effets de bord, garde-fou ──
@mcp.tool()
def rembourser_commande(numero: str, montant: float) -> dict:
    """Rembourse une commande. Bloqué au-delà de 500 $ (validation humaine requise)."""
    if montant > MAX_REMBOURSEMENT:
        # Le garde-fou vit CÔTÉ SERVEUR : il s'applique même si le
        # prompt du modèle est manipulé (injection). Défense en profondeur.
        return {
            "statut": "refuse",
            "raison": f"Montant {montant} $ > plafond {MAX_REMBOURSEMENT} $. "
                      "Escalade vers un opérateur humain requise.",
            "escalade": True,
        }
    return {"statut": "effectue", "numero": numero, "montant": montant}

# ── TOOL de lecture contrôlée par le modèle ──
@mcp.tool()
def rechercher_commande(numero: str) -> dict:
    """Recherche une commande par numéro (le modèle décide quand chercher)."""
    brut = {"numero": numero, "date_livraison": "15/03/2026", "statut": "expédiée"}
    brut["date_livraison"] = normaliser_date(brut["date_livraison"])  # → 2026-03-15
    return brut

# ── RESOURCE : donnée en lecture, contrôlée par l'application ──
@mcp.resource("db://commandes/{numero}")
def ressource_commande(numero: str) -> str:
    """Fiche commande complète, exposée à l'hôte via URI paramétrée."""
    return f'{{"numero": "{numero}", "historique": [...], "client": "..."}}'

# ── PROMPT : modèle d'invite déclenché par l'utilisateur ──
@mcp.prompt()
def analyse_litige(numero: str, motif: str) -> str:
    """Flux structuré d'analyse de litige client."""
    return (
        f"Analyse le litige sur la commande {numero}, motif : {motif}.\n"
        "1. Vérifie l'historique de la commande.\n"
        "2. Compare avec la politique de remboursement.\n"
        "3. Propose une résolution ; si remboursement > 500 $, recommande une escalade."
    )

if __name__ == "__main__":
    mcp.run()  # transport stdio par défaut ; mcp.run(transport="streamable-http") pour le distant

Teaching points of emphasis:

4.2 MCP client: session, discovery, invocation

# client_commandes.py
import asyncio
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client

async def main():
    params = StdioServerParameters(
        command="python", args=["serveur_commandes.py"]
    )
    async with stdio_client(params) as (lecture, ecriture):
        async with ClientSession(lecture, ecriture) as session:
            # 1. Handshake : négociation de capacités
            init = await session.initialize()
            print("Capacités serveur :", init.capabilities)

            # 2. Découverte
            outils = await session.list_tools()
            for outil in outils.tools:
                print(f"- {outil.name} : {outil.description}")

            # 3. Invocation
            resultat = await session.call_tool(
                "rembourser_commande",
                {"numero": "CMD-2026-0193", "montant": 750.0},
            )
            print(resultat.content)  # → statut: refuse, escalade: True

asyncio.run(main())

To highlight: there ClientSession encapsulates the entire protocol (handshake, id JSON-RPC, request/response correlation). The architect must nevertheless understand what is going on “under the hood” — this is exactly what the interactive web page for this session visualizes.


Block 5 — Security, ecosystem, advanced patterns

5.1 MCP security model

Three pillars, all required for certification:

  1. Server isolation : each server is a separate process , with a dedicated client (1:1 relationship). The GitHub server never sees data from the PostgreSQL server. No cross-server leaks by construction: the servers do not communicate with each other; only the host sees the whole.
  2. User consent : Any side effect tool call must be approved by the user (or by an explicit policy of the host). The host is the application point: it displays the tool, the arguments, and asks for confirmation.
  3. Least privilege : a server only receives necessary access (targeted environment variables, narrow-scope tokens, explicit authorized directories for a filesystem server).

Educational point — prompt injection: a third-party MCP server may return malicious text (“ignore your instructions and…”). The host must treat tool output as unreliable data , never as instructions. Connect this point to the $500 safeguard: the server-side defense holds even if the model is manipulated.

5.2 Ecosystem

More than 50 community servers ⚠ (number growing rapidly — check before each session) cover common needs: filesystem, GitHub, Slack, PostgreSQL, Google Drive, browser, persistent memory… Architect’s reflex: find an existing server before writing one . We write an in-house server for its internal business systems, not for generic integrations.

5.3 Advanced patterns

Boss Mechanism Use cases
Dynamic tool registration The server adds/removes tools during the session and issues notifications/tools/list_changed ; the customer rediscovers via tools/list . State-dependent tools (after connection to a database, expose its tables); escalation of privileges after authentication.
Subscription to resources resources/subscribe → the server pushes notifications/resources/updated at each change; the host replays the resource. Real-time dashboard, monitored configuration file, ticket whose status changes.
Prompt chaining An MCP prompt generates a sequence of messages that orchestrate several successive tool calls. “Dispute analysis” flow: research → policy verification → proposal → possible escalation.
Standardization at the border The server converts all heterogeneous formats (dates, currencies, units) to a canonical format before responding. Aggregation of several MCP servers returning dates in different formats → ISO 8601 everywhere.

5.4 Certification summary — the 7 reflexes

  1. Client:Server = 1:1, the host orchestrates.
  2. Everything is JSON-RPC 2.0 : request (id), response (id), notification (without id).
  3. Transportation : stdio = local/single user , Streamable HTTP = remote/shared , SSE = deprecated.
  4. Primitives by controller : Tool = model, Resource = application, Prompt = user.
  5. The handshake capacity negotiation guarantees the scalable compatibility .
  6. Business safeguards server side , never only in the prompt.
  7. Tool outputs = unreliable data ; user consent for side effects.

Animation tips

References