Introduction
Le Model Context Protocol (MCP) est devenu le connecteur standard entre les assistants IA et les outils externes, sources de données et services. FastMCP — désormais en version 3.2 — propulse environ 70 % des serveurs MCP en production, tous langages confondus.
Si vous devez exposer une base de données, une API interne, un système de fichiers ou toute logique métier à Claude Desktop, Claude Code, Cursor ou Cline, FastMCP est la voie la plus rapide. Son API basée sur des décorateurs rappelle Flask et FastAPI, permettant aux développeurs Python de lancer un serveur en quelques minutes.
Dans ce guide, vous construirez un serveur MCP complet comprenant outils, ressources, templates de prompts, injection de dépendances, authentification par token Bearer et déploiement Docker — tout ce qu'il faut pour passer en production.
Prérequis
- Python 3.10 ou supérieur (3.12+ recommandé pour les meilleures performances)
- Gestionnaire de paquets UV (ou pip)
- Une version récente de Claude Desktop, Cursor ou Cline pour les tests locaux
- Notions de base de Python asynchrone
Ce que vous allez construire
Un Serveur d'intelligence projets — un serveur MCP qui donne à tout assistant IA accès à :
- Un outil
search_projectspour interroger une base de projets en mémoire - Un outil
get_project_statuspour récupérer des métadonnées de projet en temps réel - Une ressource
projects://listexposant un instantané statique de tous les projets - Un template de prompt
status_reportguidant le résumé des données - Un transport HTTP avec authentification Bearer Token pour la production
Étape 1 : Configuration du projet
Créez un nouveau projet avec UV et installez FastMCP :
uv init project-mcp-server
cd project-mcp-server
uv add fastmcpOu avec pip :
mkdir project-mcp-server && cd project-mcp-server
python -m venv .venv && source .venv/bin/activate
pip install fastmcpVérifiez l'installation :
fastmcp version
# FastMCP 3.2.4Créez le fichier serveur principal :
touch server.pyÉtape 2 : Initialisation du serveur
Ouvrez server.py et configurez l'instance FastMCP :
from fastmcp import FastMCP, Context
mcp = FastMCP(
name="Project Intelligence Server",
instructions=(
"Vous avez accès à une base de données de projets. "
"Utilisez search_projects pour chercher par mot-clé, "
"et get_project_status pour récupérer les métadonnées en direct."
),
)Le paramètre instructions est envoyé à l'IA en début de session pour décrire les capacités de votre serveur.
Étape 3 : Définir les outils
Les outils sont les primitives MCP les plus courantes — des fonctions appelables que l'IA invoque pour agir ou récupérer des données calculées.
FastMCP infère automatiquement le schéma des entrées à partir des annotations de type et des docstrings :
PROJECTS = [
{"id": "p1", "name": "Plateforme Noqta", "status": "active", "lang": "TypeScript"},
{"id": "p2", "name": "API Facturation", "status": "active", "lang": "Python"},
{"id": "p3", "name": "Application Mobile", "status": "paused", "lang": "React Native"},
{"id": "p4", "name": "Pipeline de données", "status": "archived", "lang": "Python"},
]
@mcp.tool
def search_projects(keyword: str, status: str = "all") -> list[dict]:
"""
Recherche des projets par mot-clé et filtre optionnel de statut.
Args:
keyword: Terme de recherche correspondant au nom ou au langage du projet.
status: Filtrer par statut : active, paused, archived ou all.
"""
results = PROJECTS
if status != "all":
results = [p for p in results if p["status"] == status]
keyword = keyword.lower()
return [p for p in results if keyword in p["name"].lower() or keyword in p["lang"].lower()]
@mcp.tool
async def get_project_status(project_id: str, ctx: Context) -> dict:
"""
Récupère le statut détaillé d'un projet par son identifiant.
Args:
project_id: Identifiant unique du projet (ex. p1, p2).
"""
await ctx.info(f"Récupération du statut pour le projet {project_id}")
project = next((p for p in PROJECTS if p["id"] == project_id), None)
if project is None:
raise ValueError(f"Projet '{project_id}' introuvable")
return {
**project,
"last_commit": "2026-06-24",
"open_issues": 3,
"coverage": "87%",
}Points clés :
- Les fonctions synchrones conviennent aux tâches CPU ;
async defest nécessaire pour les E/S. - Le paramètre
ctx: Contextest injecté automatiquement — l'IA ne le passe pas manuellement. ctx.info(),ctx.debug(),ctx.warning()émettent des messages de log affichés par le client.- Lever une exception Python standard produit un message d'erreur clair pour l'IA.
Étape 4 : Définir les ressources
Les ressources exposent des données en lecture plutôt que calculées. Elles sont idéales pour la configuration, la documentation ou les données statiques :
import json
@mcp.resource("projects://list")
def list_all_projects() -> str:
"""Instantané de tous les projets du système."""
return json.dumps(PROJECTS, ensure_ascii=False, indent=2)
@mcp.resource("projects://{project_id}/details")
def project_details(project_id: str) -> str:
"""Vue détaillée d'un projet par son identifiant."""
project = next((p for p in PROJECTS if p["id"] == project_id), None)
if project is None:
return json.dumps({"error": f"Projet {project_id} introuvable"}, ensure_ascii=False)
return json.dumps(project, ensure_ascii=False, indent=2)Les templates d'URI avec {placeholder} sont automatiquement mappés aux paramètres de la fonction.
Étape 5 : Définir les templates de prompts
Les prompts sont des instructions réutilisables qui guident le raisonnement de l'IA. Ils apparaissent dans les interfaces clients comme templates sélectionnables :
@mcp.prompt
def status_report(project_id: str, audience: str = "engineering") -> str:
"""
Génère un template de rapport d'état pour un projet et un public cibles.
Args:
project_id: Identifiant du projet cible.
audience: Public visé : engineering, management ou client.
"""
tone = {
"engineering": "technique, axé sur les métriques et les blocages",
"management": "concis, axé sur le calendrier et les risques",
"client": "non technique, positif, axé sur la valeur livrée",
}.get(audience, "équilibré")
return (
f"À partir des données du projet '{project_id}', rédigez un rapport d'état {tone}. "
"Incluez : statut actuel, progrès récents, problèmes ouverts et prochaines étapes. "
"Maximum 300 mots."
)Étape 6 : Injection de dépendances avec Depends
Quand plusieurs outils partagent une ressource coûteuse — connexion DB, client HTTP, cache — utilisez Depends pour l'initialiser une seule fois par requête :
from contextlib import asynccontextmanager
from fastmcp.dependencies import Depends
async def get_db():
"""Simule un client DB. En production, remplacez par une vraie connexion."""
class FakeDB:
async def query(self, sql: str) -> list:
return [{"result": f"Exécuté : {sql}"}]
db = FakeDB()
yield db
@mcp.tool
async def run_query(sql: str, db=Depends(get_db)) -> list:
"""
Exécute une requête SQL en lecture seule sur la base de projets.
Args:
sql: Une instruction SELECT à exécuter.
"""
return await db.query(sql)Étape 7 : État au niveau du serveur avec lifespan
Pour un état devant persister toute la durée du serveur (pool de connexions, modèle chargé), utilisez l'API lifespan :
from fastmcp.server.lifespan import lifespan
@lifespan
async def app_lifespan(server):
"""Initialise et nettoie les ressources côté serveur."""
print("Démarrage — construction de l'index projets...")
index = {p["id"]: p for p in PROJECTS}
yield {"index": index}
print("Arrêt — libération de l'index.")
mcp_with_lifespan = FastMCP(
name="Project Intelligence Server",
lifespan=app_lifespan,
)
@mcp_with_lifespan.tool
def fast_lookup(project_id: str, ctx: Context) -> dict:
"""Recherche instantanée via l'index pré-construit."""
index = ctx.lifespan_context["index"]
return index.get(project_id, {"error": "introuvable"})Étape 8 : Tests avec MCP Inspector
FastMCP intègre un support natif pour MCP Inspector — une interface de test dans le navigateur :
fastmcp dev inspector server.pyCette commande lance un serveur local avec rechargement automatique et ouvre Inspector sur http://localhost:6274. Vous pouvez :
- Parcourir les outils, ressources et prompts enregistrés
- Appeler des outils directement avec des arguments personnalisés
- Inspecter le cycle requête/réponse complet
Étape 9 : Connexion aux clients IA
Claude Desktop
Ajoutez votre serveur dans ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) :
{
"mcpServers": {
"project-intelligence": {
"command": "fastmcp",
"args": ["run", "/chemin/absolu/vers/server.py"]
}
}
}Redémarrez Claude Desktop. Vos outils apparaîtront dans la liste des outils disponibles.
Claude Code
claude mcp add project-intelligence fastmcp run /chemin/absolu/vers/server.pyCursor / Cline
Dans les paramètres Cursor → MCP Servers :
{
"name": "project-intelligence",
"command": "fastmcp run /chemin/absolu/vers/server.py"
}Étape 10 : Transport HTTP avec authentification
Pour un déploiement distant ou un serveur partagé en équipe, passez au transport HTTP avec authentification Bearer Token :
from fastmcp.server.auth import StaticTokenVerifier
import os
auth = StaticTokenVerifier(
tokens={
os.environ["MCP_TOKEN_PROD"]: {"client_id": "prod", "scopes": ["read", "write"]},
os.environ["MCP_TOKEN_READONLY"]: {"client_id": "viewer", "scopes": ["read"]},
},
required_scopes=["read"],
)
secured_mcp = FastMCP(
name="Project Intelligence Server",
auth=auth,
)
if __name__ == "__main__":
secured_mcp.run(transport="http", host="0.0.0.0", port=8000)Lancez le serveur sécurisé :
MCP_TOKEN_PROD=votre-token-secret \
MCP_TOKEN_READONLY=votre-token-lecture \
python server.pyConnectez un client avec le token :
uvx fastmcp-remote https://votre-serveur.example.com/mcp \
--header "Authorization: Bearer votre-token-secret"Étape 11 : Déploiement Docker
Créez un Dockerfile :
FROM python:3.12-slim
WORKDIR /app
RUN pip install uv
COPY pyproject.toml uv.lock* ./
RUN uv sync --frozen --no-dev
COPY server.py .
EXPOSE 8000
CMD ["uv", "run", "fastmcp", "run", "server.py", \
"--transport", "http", "--host", "0.0.0.0", "--port", "8000"]Construisez et lancez :
docker build -t project-mcp-server .
docker run -p 8000:8000 \
-e MCP_TOKEN_PROD=votre-token-secret \
project-mcp-serverPour les équipes utilisant Docker Compose :
services:
mcp-server:
build: ./mcp
ports:
- "8000:8000"
environment:
MCP_TOKEN_PROD: ${MCP_TOKEN_PROD}
restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
interval: 30s
timeout: 10s
retries: 3Étape 12 : Traçage avec OpenTelemetry
FastMCP 3.x inclut un support OpenTelemetry natif pour l'observabilité en production :
from fastmcp.telemetry import get_tracer
@mcp.tool
async def search_projects_traced(keyword: str, ctx: Context) -> list[dict]:
"""Recherche de projets avec traçage distribué."""
tracer = get_tracer()
with tracer.start_as_current_span("search.filter") as span:
span.set_attribute("search.keyword", keyword)
results = [p for p in PROJECTS if keyword.lower() in p["name"].lower()]
span.set_attribute("search.result_count", len(results))
await ctx.info(f"Trouvé {len(results)} projet(s) pour '{keyword}'")
return resultsEnvoyez les traces vers Grafana, Jaeger ou Datadog via des variables d'environnement :
OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector:4317 \
OTEL_SERVICE_NAME=project-mcp-server \
python server.pyRésolution des problèmes courants
L'outil n'apparaît pas dans Claude Desktop
- Vérifiez que le chemin dans
claude_desktop_config.jsonest absolu, non relatif. - Assurez-vous que
fastmcpest dans le PATH utilisé par Claude Desktop. - Redémarrez complètement Claude Desktop après toute modification de config.
TypeError: cannot unpack non-iterable NoneType object
Cela signifie généralement que le générateur de dépendance a retourné None au lieu de yield. Assurez-vous que votre fonction Depends utilise yield, pas return.
Erreurs d'authentification sur transport HTTP
- Vérifiez que le header
Authorization: Bearer <token>est présent et correctement formaté. - Confirmez que
required_scopescorrespond aux scopes assignés au token dansStaticTokenVerifier.
Erreurs de syntaxe sur Python 3.10 avec les annotations de type
Ajoutez from __future__ import annotations en tête de fichier, ou passez à Python 3.12 où la syntaxe X | Y est pleinement supportée.
Prochaines étapes
- Explorez l'intégration OpenAPI de FastMCP pour générer automatiquement un serveur MCP depuis une spec REST existante.
- Ajoutez une limitation de débit (rate limiting) via un compteur Redis dans une dépendance
Depends. - Connectez votre serveur à LangGraph ou PydanticAI via
FastMCPToolsetpour des workflows multi-agents. - Consultez le tutoriel Construire un serveur MCP en TypeScript pour l'équivalent côté JS.
Conclusion
FastMCP 3.x offre aux développeurs Python un chemin clair et production-ready vers l'écosystème MCP. Avec des décorateurs pour les outils, ressources et prompts, l'injection de dépendances, l'authentification Bearer Token et un transport HTTP prêt pour Docker, vous pouvez déployer un vrai serveur MCP en quelques heures. Ce même serveur se connecte à Claude Desktop, Claude Code, Cursor et Cline sans aucun code spécifique au client — une seule implémentation pour tous les assistants IA.