Aller au contenu principal
Leçon 12 sur 14 · MCP pour Développeurs — Serveurs et Intégrations

Déployer un Serveur MCP en Production

Leçon 12 sur 14 Mis à jour le 2 novembre 2026 Développeur
Sur cette page
  1. Du prototype au serveur de production
  2. Option 1 : Hugging Face Spaces
  3. Option 2 : Docker
  4. Options 3 et 4 : déploiements managés
  5. Sécuriser le déploiement
  6. Voir ce qui se passe
  7. Points clés à retenir

Mise à jour du 2 novembre 2026 — Le Chat devient Vibe. Depuis le 28 mai 2026, Le Chat s’appelle Vibe (toujours sur chat.mistral.ai) : un seul agent pour le travail et le code, avec vos conversations, réglages et abonnements repris. L’ancienne expérience Le Chat reste accessible dans le mode Chat ; dans le mode Work, les agents sont devenus des skills (invoquées avec @), les mémoires la base de connaissances et la recherche approfondie la skill Deep Research. Les offres s’appellent désormais Mistral Free, Pro, Team et Enterprise. Cette leçon emploie désormais le nom Vibe ; certains menus décrits ici ont pu changer de nom ou de place.

Mise à jour du 2 novembre 2026 — SDK Python MCP v2. Ce cours suit désormais la version 2 du SDK mcp (2.3.0 au 2 octobre 2026) : MCPServer remplace FastMCP, les réglages de transport passent à run() ou à streamable_http_app(), et une application exposée sous un vrai nom d’hôte doit déclarer ce nom (transport_security). C’est aussi la version qu’exige le SDK Mistral : l’extra mistralai[agents] demande mcp>=2.2.0,<3.0. Sources : What’s new in v2 · Guide de migration · Deploy & scale.

Du prototype au serveur de production

Votre serveur tourne en local et ngrok l’expose au monde le temps d’une démonstration. Le problème apparaît dès que quelqu’un d’autre veut s’en servir : vous fermez votre terminal, l’URL meurt, et tous les connecteurs configurés chez vos collègues tombent en panne. Passer en production, c’est essentiellement obtenir une adresse stable, disponible en permanence, servie en HTTPS. Quatre options y mènent, de la plus immédiate à la plus maîtrisée, et le choix dépend surtout de la sensibilité de vos données et du contrôle que vous voulez garder sur l’infrastructure.

Option 1 : Hugging Face Spaces

Hugging Face Spaces met à disposition des machines virtuelles gratuites, sans GPU, largement suffisantes pour un serveur MCP qui relaie des appels d’API. C’est la voie la plus rapide vers un premier déploiement réel : trois fichiers et un git push.

mon-mcp-space/
├── app.py              # Serveur FastAPI + MCP
├── requirements.txt    # Dépendances
└── README.md           # Metadata du Space

Le fichier app.py reprend exactement la structure vue jusqu’ici, à ceci près que les valeurs sensibles sont lues dans l’environnement — les Secrets HF les y injectent :

# app.py
"""Serveur MCP déployé sur Hugging Face Spaces."""

import os
from contextlib import asynccontextmanager

from fastapi import FastAPI
from mcp.server.mcpserver import MCPServer
from mcp.server.transport_security import TransportSecuritySettings

mcp = MCPServer("Mon Serveur Production")

@mcp.tool()
def hello(name: str) -> str:
    """Salue une personne.

    Args:
        name: Le prénom de la personne

    Returns:
        Message de salutation
    """
    return f"Bonjour {name} depuis Hugging Face Spaces !"

@asynccontextmanager
async def lifespan(app: FastAPI):
    async with mcp.session_manager.run():
        yield

# Nom d'hôte du Space, de la forme <utilisateur>-<space>.hf.space
SPACE_HOST = os.environ.get("MCP_PUBLIC_HOST", "localhost")
securite = TransportSecuritySettings(allowed_hosts=[SPACE_HOST, f"{SPACE_HOST}:*"])

api = FastAPI(lifespan=lifespan)
api.mount("/mcp-endpoint", mcp.streamable_http_app(transport_security=securite))

# Pour les variables sensibles, utilisez les Secrets HF
API_KEY = os.environ.get("MY_API_KEY", "")

La particularité de la plateforme tient au README, dont l’en-tête YAML pilote la construction du Space. Le champ app_port doit correspondre au port réellement écouté par votre serveur, faute de quoi le déploiement réussit mais reste inaccessible :

---
title: Mon Serveur MCP
emoji: 🔧
colorFrom: blue
colorTo: green
sdk: docker
app_port: 7860
---
# Créer un Space sur huggingface.co
# Pousser le code
git push
# Le Space se déploie automatiquement

Votre endpoint devient alors https://votre-username-mon-mcp-space.hf.space/mcp-endpoint/mcp, en HTTPS. Une seule configuration reste à faire : déclarer ce nom d’hôte, votre-username-mon-mcp-space.hf.space, dans la variable MCP_PUBLIC_HOST des réglages du Space. Sans elle, le SDK refuse chaque requête avec une erreur 421 Invalid Host header.

Option 2 : Docker

Dès que le serveur touche à des données internes, l’hébergement sur votre propre infrastructure redevient la règle, et Docker en est le format standard. Le Dockerfile tient en quelques instructions, avec l’installation des dépendances placée avant la copie du code pour profiter du cache de couches lors des rebuilds :

FROM python:3.12-slim

WORKDIR /app

# Installer les dépendances
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# Copier le code
COPY . .

# Exposer le port
EXPOSE 8000

# Lancer le serveur
CMD ["uvicorn", "server:api", "--host", "0.0.0.0", "--port", "8000"]
fastapi>=0.115.0
uvicorn>=0.30.0
mcp>=2.2,<3
httpx>=0.27.0   # seulement si vos outils l'utilisent : le SDK v2 ne l'installe plus

Au lancement, l’option --restart unless-stopped est celle qui distingue un déploiement de production d’un simple essai : sans elle, le premier crash met votre serveur hors ligne jusqu’à ce que quelqu’un s’en aperçoive.

# Build l'image
docker build -t mon-mcp-server .

# Lancer le conteneur
docker run -d \
  --name mcp-server \
  -p 8000:8000 \
  -e MY_API_KEY="secret" \
  -e DATABASE_URL="postgresql://..." \
  --restart unless-stopped \
  mon-mcp-server

Docker Compose rend cette configuration reproductible et y ajoute un health check, que l’orchestrateur utilisera pour détecter un serveur figé — un processus vivant mais qui ne répond plus est le scénario que le simple redémarrage automatique ne couvre pas :

# docker-compose.yml
version: "3.8"
services:
  mcp-server:
    build: .
    ports:
      - "8000:8000"
    environment:
      - MY_API_KEY=${MY_API_KEY}
      - DATABASE_URL=${DATABASE_URL}
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8000/"]
      interval: 30s
      retries: 3

Options 3 et 4 : déploiements managés

Prefect Horizon (anciennement FastMCP Cloud), la plateforme de l’équipe du framework indépendant FastMCP, supprime l’étape d’infrastructure : vous vous connectez sur horizon.prefect.io avec votre compte GitHub, vous choisissez le dépôt et le fichier du serveur, et la plateforme construit puis publie une URL, avec authentification intégrée. Elle vise les serveurs écrits avec ce framework (pip install fastmcp, from fastmcp import FastMCP), distinct du SDK officiel utilisé dans ce cours ; une offre gratuite existe pour les projets personnels.

Cloudflare Workers vise un autre profil d’usage : le serverless, avec une exécution au plus près de l’utilisateur et donc une latence réduite. Un template dédié amorce le projet :

# Utiliser le template MCP de Cloudflare
npm create cloudflare@latest -- remote-mcp-server-authless --template=cloudflare/ai/demos/remote-mcp-authless

Ces deux options vous dispensent de gérer les certificats et les redémarrages, au prix d’une dépendance à la plateforme et de contraintes d’exécution — un Worker serverless, par exemple, se marie mal avec un serveur stateful.

Sécuriser le déploiement

Le HTTPS n’est pas négociable : Vibe refuse les endpoints en clair, et un token transitant en HTTP est un token compromis. Trois chemins y mènent selon votre hébergement. Un reverse proxy Nginx ou Caddy placé devant votre serveur, avec un certificat Let’s Encrypt, couvre le cas du serveur dédié. Les plateformes cloud natives — Hugging Face, Cloudflare et la plupart des PaaS — fournissent le HTTPS automatiquement. Un tunnel, Cloudflare Tunnel par exemple, expose un serveur resté sur votre réseau interne sans ouvrir de port.

Aucun secret ne doit figurer dans le code. Notez la distinction entre les deux syntaxes ci-dessous : os.environ["MCP_API_TOKEN"] fait échouer le démarrage si la variable manque, ce qui est exactement le comportement souhaité pour un secret obligatoire, tandis que os.environ.get() tolère l’absence pour les valeurs optionnelles.

import os

# Configuration via variables d'environnement
API_TOKEN = os.environ["MCP_API_TOKEN"]       # Obligatoire
DATABASE_URL = os.environ.get("DATABASE_URL")  # Optionnel
DEBUG = os.environ.get("DEBUG", "false") == "true"

Un serveur public sans limitation de débit finit tôt ou tard saturé, volontairement ou non. Un limiteur par adresse IP suffit dans la plupart des cas :

from fastapi import FastAPI, Request
from slowapi import Limiter, _rate_limit_exceeded_handler

limiter = Limiter(key_func=lambda request: request.client.host)
api = FastAPI()
api.state.limiter = limiter

@api.middleware("http")
async def rate_limit(request: Request, call_next):
    # 100 requêtes par minute par IP
    ...

Voir ce qui se passe

En production, vous n’avez plus votre terminal sous les yeux, et un tool qui échoue une fois sur dix ne se diagnostique qu’avec des traces. Instrumentez au minimum l’entrée, la sortie et la durée de chaque appel :

import logging
import time

logger = logging.getLogger("mcp-server")

@mcp.tool()
async def my_tool(param: str) -> str:
    """Mon tool avec logging."""
    start = time.time()
    logger.info(f"Tool appelé avec param={param}")

    try:
        result = await process(param)
        duration = time.time() - start
        logger.info(f"Tool terminé en {duration:.2f}s")
        return result
    except Exception as e:
        logger.error(f"Erreur dans le tool : {e}")
        raise

La mesure de durée n’est pas un luxe : un tool qui met douze secondes à répondre déclenche des timeouts côté client et donne l’impression d’un serveur cassé alors qu’il fonctionne. Avant d’annoncer votre serveur, passez en revue les six points de configuration suivants.

  • HTTPS actif et certificat valide
  • Variables d’environnement pour tous les secrets
  • Logs structurés activés
  • Health check endpoint (/ ou /health)
  • Restart automatique en cas de crash
  • Rate limiting configuré

Restent deux vérifications, qu’on saute le plus volontiers et qui pourtant révèlent les vrais problèmes : un test avec MCP Inspector lancé depuis l’extérieur, puis un test depuis Vibe de Mistral. Un serveur qui répond parfaitement depuis votre machine peut être bloqué par un pare-feu ou une configuration CORS dès qu’on l’appelle d’ailleurs.

Points clés à retenir

  • Hugging Face Spaces : le plus rapide, gratuit, idéal pour commencer
  • Docker : contrôle total, adapté aux serveurs dédiés et au cloud
  • Prefect Horizon (ex-FastMCP Cloud) : déploiement depuis GitHub, pour les serveurs écrits avec le framework FastMCP
  • Cloudflare Workers : serverless, faible latence, scalable
  • Sécurisez avec HTTPS, variables d’environnement, et rate limiting
  • Ajoutez du monitoring et des logs pour le debugging en production