Applied AI · AvancĂ© 🔮 · Session 5
📝 Guide du professeur
← Retour au programme 📄 Source .md

Applied AI — Niveau AvancĂ©

Session 5 : MCP en profondeur

Programme : Applied AI — Formation professionnelle en intelligence artificielle Instructeur : Yann Isola Niveau : AvancĂ© — Architectes solutions prĂ©parant la certification Claude Certified Architect DurĂ©e recommandĂ©e : 3 h 30 (2 h de cours magistral + 1 h 30 d’exercices pratiques) PrĂ©requis : Sessions 1 Ă  4 du niveau avancĂ© (orchestration, outils/function calling, gestion de contexte, architecture multi-agents)


Objectifs pédagogiques

À l’issue de cette session, les participants seront capables de :

  1. DĂ©crire l’architecture complĂšte du protocole MCP (Model Context Protocol — protocole de contexte de modĂšle) : HĂŽte ↔ Client ↔ Serveur, et le rĂŽle exact de chaque composant.
  2. Choisir et justifier une couche de transport (stdio local vs Streamable HTTP distant) selon des critĂšres d’architecture, de sĂ©curitĂ© et de dĂ©ploiement.
  3. Distinguer les trois primitives MCP — Tools, Resources, Prompts — et sĂ©lectionner la bonne primitive selon qui contrĂŽle l’invocation (le modĂšle, l’application ou l’utilisateur). C’est un point d’examen central de la certification.
  4. Construire un serveur MCP complet en Python (SDK officiel) exposant outils, ressources et prompts, avec garde-fous intégrés.
  5. Construire un client MCP : gestion de session, dĂ©couverte d’outils, nĂ©gociation de capacitĂ©s.
  6. Appliquer les patrons avancĂ©s : enregistrement dynamique d’outils, abonnement aux ressources avec notifications, chaĂźnage de prompts, normalisation de donnĂ©es hĂ©tĂ©rogĂšnes.

Plan de la session

Bloc Durée Contenu
1 30 min Architecture MCP : HĂŽte, Client, Serveur, JSON-RPC
2 25 min Couche de transport : stdio vs Streamable HTTP
3 35 min Les trois primitives : Tools, Resources, Prompts
4 30 min Construire un serveur et un client MCP (SDK Python)
5 20 min Sécurité, écosystÚme et patrons avancés
6 90 min Exercices pratiques + démonstration interactive (page web)
7 10 min Quiz de validation et synthĂšse certification

Bloc 1 — Architecture MCP : Hîte, Client, Serveur

1.1 Le problÚme que MCP résout

Avant MCP, chaque Ă©diteur d’application IA devait Ă©crire un connecteur spĂ©cifique pour chaque outil externe : N applications × M outils = N×M intĂ©grations. C’est le problĂšme classique du « M×N » que les standards rĂ©solvent (comme USB-C l’a fait pour la connectique, ou LSP — Language Server Protocol, protocole de serveur de langage — pour les Ă©diteurs de code).

MCP transforme M×N en M+N : chaque outil expose une seule interface standard (un serveur MCP), chaque application implĂ©mente une seule interface standard (un client MCP). Tout serveur fonctionne avec tout hĂŽte compatible.

Point pĂ©dagogique : l’analogie USB-C est officiellement utilisĂ©e par Anthropic. Les participants la retrouveront dans la documentation de certification. Faites-la reformuler par un participant : « MCP est Ă  l’IA ce que USB-C est au matĂ©riel : un port universel. »

1.2 Les trois composants

┌─────────────────────────── 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│
    └──────────┘      └──────────┘      └──────────┘
Composant RĂŽle Exemples
HĂŽte (Host) L’application qui embarque le modĂšle et pilote l’expĂ©rience utilisateur. DĂ©cide quels serveurs connecter, applique les politiques de sĂ©curitĂ© et de consentement. Claude Desktop, un IDE (Cursor, VS Code), une application mĂ©tier
Client (Client) Composant gĂ©rĂ© par le SDK, un client par serveur (relation 1:1 stricte). Maintient la session, effectue la nĂ©gociation de capacitĂ©s, route les messages. InstanciĂ© par le SDK MCP dans l’hĂŽte
Serveur (Server) Fournisseur de capacités : expose Tools, Resources et Prompts via le protocole. Processus indépendant. Serveur filesystem, serveur GitHub, serveur maison

RĂšgle Ă  marteler pour la certification : la relation Client:Serveur est 1:1. Un hĂŽte qui se connecte Ă  3 serveurs instancie 3 clients. Cette isolation est un choix d’architecture dĂ©libĂ©rĂ© (voir Bloc 5 : sĂ©curitĂ©).

1.3 JSON-RPC 2.0 : la langue commune

Tous les messages MCP sont des messages JSON-RPC 2.0 (Remote Procedure Call — appel de procĂ©dure distant en JSON). Trois types de messages :

  1. RequĂȘte (Request) — attend une rĂ©ponse, porte un id :
{
  "jsonrpc": "2.0",
  "id": 42,
  "method": "tools/call",
  "params": {
    "name": "rechercher_commande",
    "arguments": { "numero": "CMD-2026-0193" }
  }
}
  1. RĂ©ponse (Response) — porte le mĂȘme id, contient result ou error :
{
  "jsonrpc": "2.0",
  "id": 42,
  "result": {
    "content": [
      { "type": "text", "text": "{\"statut\": \"expédiée\", \"montant\": 129.90}" }
    ]
  }
}
  1. Notification (Notification) — pas d’id, pas de rĂ©ponse attendue :
{
  "jsonrpc": "2.0",
  "method": "notifications/resources/updated",
  "params": { "uri": "db://commandes/CMD-2026-0193" }
}

1.4 Le cycle de vie d’une session

  1. initialize : le client envoie sa version de protocole et ses capacitĂ©s (ce qu’il sait gĂ©rer : sampling, notifications, etc.). Le serveur rĂ©pond avec ses propres capacitĂ©s (tools, resources, prompts, subscriptions
).
  2. notifications/initialized : le client confirme — la session est ouverte.
  3. Découverte : tools/list, resources/list, prompts/list.
  4. Opérations : tools/call, resources/read, prompts/get, abonnements

  5. Fermeture : terminaison propre du transport.

Point certification — nĂ©gociation de capacitĂ©s : le client dĂ©clare ce qu’il supporte au handshake (poignĂ©e de main initiale). Un serveur ne doit jamais envoyer une notification d’abonnement Ă  un client qui n’a pas dĂ©clarĂ© la capacitĂ© correspondante. À l’examen, on vous demandera pourquoi la nĂ©gociation existe : pour permettre l’évolution du protocole sans casser la compatibilitĂ© — un client ancien et un serveur rĂ©cent peuvent coopĂ©rer sur l’intersection de leurs capacitĂ©s.


Bloc 2 — Couche de transport : stdio vs Streamable HTTP

2.1 stdio : le transport local

Le serveur est lancĂ© comme sous-processus de l’hĂŽte. Les messages JSON-RPC circulent sur stdin/stdout (entrĂ©e/sortie standard), un message JSON par ligne.

Caractéristiques :

Configuration typique (Claude Desktop) :

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

2.2 Streamable HTTP : le transport distant

Pour les serveurs distants (mutualisĂ©s, multi-utilisateurs, cloud), MCP utilise Streamable HTTP, qui remplace l’ancien transport SSE (Server-Sent Events — Ă©vĂ©nements envoyĂ©s par le serveur), dĂ©prĂ©ciĂ©.

Fonctionnement :

Caractéristiques :

2.3 Matrice de dĂ©cision (Ă  connaĂźtre pour l’examen)

CritĂšre stdio Streamable HTTP
Localisation MĂȘme machine que l’hĂŽte Machine distante / cloud
Nombre d’utilisateurs 1 (l’utilisateur local) N (mutualisĂ©)
Authentification HĂ©ritĂ©e du systĂšme d’exploitation OAuth 2.1 / jetons
Latence Minimale Réseau (variable)
DĂ©ploiement DistribuĂ© avec l’application hĂŽte OpĂ©rĂ© comme un service web
AccÚs aux ressources locales (fichiers, périphériques) Direct Impossible (ou via tunnel)
Cas d’usage type Serveur filesystem, outils de dĂ©veloppement locaux Serveur SaaS d’entreprise, API mĂ©tier partagĂ©e

RĂšgle de poche certification : « Fichiers locaux et mono-utilisateur → stdio. Service partagĂ©, authentifiĂ©, scalable → Streamable HTTP. SSE seul → rĂ©ponse fausse (dĂ©prĂ©ciĂ©). »


Bloc 3 — Les trois primitives : Tools, Resources, Prompts

C’est le cƓur de la session et de la certification. La question n’est pas « que fait la primitive ? » mais « qui dĂ©cide de son invocation ? ».

3.1 Tableau de contrÎle (à mémoriser)

Primitive Qui contrÎle ? Déclencheur Analogie
Tool (outil) Le modĂšle (model-controlled) Le LLM dĂ©cide d’appeler l’outil pendant son raisonnement Les mains de l’agent
Resource (ressource) L’application (application-controlled) L’hĂŽte dĂ©cide quelles donnĂ©es injecter dans le contexte Les yeux de l’agent — lecture seule
Prompt (invite) L’utilisateur (user-controlled) L’humain choisit explicitement un modĂšle d’invite (menu, commande slash) Un formulaire prĂ©-rempli

3.2 Tools : invoqués par le modÚle

{
  "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 : exposĂ©es par l’application

Pourquoi « application-controlled » ? Parce que c’est l’hĂŽte — pas le modĂšle — qui dĂ©cide quelles ressources charger dans le contexte. Cela protĂšge la fenĂȘtre de contexte (l’hĂŽte filtre) et la confidentialitĂ© (le modĂšle ne « fouille » pas librement).

3.4 Prompts : dĂ©clenchĂ©s par l’utilisateur

{
  "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 Arbre de décision (reproduit dans la page web interactive)

  1. L’action modifie un Ă©tat ou dĂ©clenche un effet de bord ? → Tool.
  2. C’est une donnĂ©e Ă  lire, et c’est l’application qui doit dĂ©cider quand l’injecter ? → Resource.
  3. C’est un flux de travail que l’humain dĂ©clenche explicitement avec des paramĂštres ? → Prompt.
  4. Le modĂšle doit dĂ©cider seul, au fil du raisonnement, d’aller chercher la donnĂ©e ? → alors mĂȘme une lecture peut ĂȘtre un Tool (ex. rechercher_client). La frontiĂšre Resource/Tool passe par qui contrĂŽle, pas par lecture/Ă©criture seule.

PiĂšge d’examen n° 1 : « une recherche dans une base est une lecture, donc c’est une Resource » — faux si c’est le modĂšle qui doit dĂ©cider de la dĂ©clencher dynamiquement. Une recherche invoquĂ©e par le modĂšle est un Tool. Une donnĂ©e prĂ©-sĂ©lectionnĂ©e par l’application est une Resource.


Bloc 4 — Construire un serveur et un client MCP (SDK Python)

4.1 Serveur complet avec garde-fous

Le SDK Python officiel (mcp, avec l’API FastMCP) fournit trois dĂ©corateurs. Exemple complet — serveur de gestion de commandes avec garde-fou de remboursement et normalisation de 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

Points d’insistance pĂ©dagogique :

4.2 Client MCP : session, découverte, 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())

À souligner : la ClientSession encapsule tout le protocole (handshake, id JSON-RPC, corrĂ©lation requĂȘte/rĂ©ponse). L’architecte doit nĂ©anmoins comprendre ce qui circule « sous le capot » — c’est exactement ce que visualise la page web interactive de cette session.


Bloc 5 — SĂ©curitĂ©, Ă©cosystĂšme, patrons avancĂ©s

5.1 ModÚle de sécurité MCP

Trois piliers, tous exigibles Ă  la certification :

  1. Isolation des serveurs : chaque serveur est un processus sĂ©parĂ©, avec un client dĂ©diĂ© (relation 1:1). Le serveur GitHub ne voit jamais les donnĂ©es du serveur PostgreSQL. Aucune fuite inter-serveurs par construction : les serveurs ne communiquent pas entre eux ; seul l’hĂŽte voit l’ensemble.
  2. Consentement utilisateur : tout appel d’outil Ă  effet de bord doit ĂȘtre approuvĂ© par l’utilisateur (ou par une politique explicite de l’hĂŽte). L’hĂŽte est le point d’application : il affiche l’outil, les arguments, et demande confirmation.
  3. Moindre privilĂšge : un serveur ne reçoit que les accĂšs nĂ©cessaires (variables d’environnement ciblĂ©es, jetons Ă  portĂ©e rĂ©duite, rĂ©pertoires autorisĂ©s explicites pour un serveur filesystem).

Point pĂ©dagogique — injection de prompt : un serveur MCP tiers peut renvoyer du texte malveillant (« ignore tes instructions et
 »). L’hĂŽte doit traiter les sorties d’outils comme des donnĂ©es non fiables, jamais comme des instructions. Reliez ce point au garde-fou des 500 $ : la dĂ©fense cĂŽtĂ© serveur tient mĂȘme si le modĂšle est manipulĂ©.

5.2 Écosystùme

Plus de 50 serveurs communautaires ⚠ (chiffre en croissance rapide — vĂ©rifier avant chaque session) couvrent les besoins courants : filesystem, GitHub, Slack, PostgreSQL, Google Drive, navigateur, mĂ©moire persistante
 RĂ©flexe d’architecte : chercher un serveur existant avant d’en Ă©crire un. On Ă©crit un serveur maison pour ses systĂšmes mĂ©tier internes, pas pour les intĂ©grations gĂ©nĂ©riques.

5.3 Patrons avancés

Patron MĂ©canisme Cas d’usage
Enregistrement dynamique d’outils Le serveur ajoute/retire des outils en cours de session et Ă©met notifications/tools/list_changed ; le client redĂ©couvre via tools/list. Outils dĂ©pendant de l’état (aprĂšs connexion Ă  une base, exposer ses tables) ; montĂ©e en privilĂšges aprĂšs authentification.
Abonnement aux ressources resources/subscribe → le serveur pousse notifications/resources/updated Ă  chaque changement ; l’hĂŽte relit la ressource. Tableau de bord temps rĂ©el, fichier de configuration surveillĂ©, ticket dont le statut Ă©volue.
ChaĂźnage de prompts Un prompt MCP gĂ©nĂšre une sĂ©quence de messages qui orchestrent plusieurs appels d’outils successifs. Flux « analyse de litige » : recherche → vĂ©rification politique → proposition → escalade Ă©ventuelle.
Normalisation Ă  la frontiĂšre Le serveur convertit tous les formats hĂ©tĂ©rogĂšnes (dates, devises, unitĂ©s) vers un format canonique avant de rĂ©pondre. AgrĂ©gation de plusieurs serveurs MCP renvoyant des dates en formats diffĂ©rents → ISO 8601 partout.

5.4 SynthĂšse certification — les 7 rĂ©flexes

  1. Client:Serveur = 1:1, l’hîte orchestre.
  2. Tout est JSON-RPC 2.0 : requĂȘte (id), rĂ©ponse (id), notification (sans id).
  3. Transport : stdio = local/mono-utilisateur, Streamable HTTP = distant/mutualisé, SSE = déprécié.
  4. Primitives par contrĂŽleur : Tool = modĂšle, Resource = application, Prompt = utilisateur.
  5. La négociation de capacités au handshake garantit la compatibilité évolutive.
  6. Garde-fous métier cÎté serveur, jamais uniquement dans le prompt.
  7. Sorties d’outils = donnĂ©es non fiables ; consentement utilisateur pour les effets de bord.

Conseils d’animation

Références