Exemple Complet : Serveur Distant avec Auth
Scénario : un serveur MCP de gestion de tâches
Dans cette leçon, vous allez créer un serveur MCP distant complet avec authentification par token. Le serveur expose des tools de gestion de tâches (créer, lister, compléter) et le client s’y connecte via HTTP avec un bearer token.
Le serveur : task_server.py
# task_server.py
"""Serveur MCP distant avec authentification token."""
import json
import uuid
from datetime import datetime
from fastapi import FastAPI, Request, HTTPException
from mcp.server.fastmcp import FastMCP
# --- Configuration ---
API_TOKEN = "mcp-secret-token-2026"
api = FastAPI(title="Task Manager MCP")
# --- Middleware d'authentification ---
@api.middleware("http")
async def auth_middleware(request: Request, call_next):
"""Vérifie le token pour toutes les routes /mcp."""
if request.url.path.startswith("/tasks"):
auth = request.headers.get("Authorization", "")
if auth != f"Bearer {API_TOKEN}":
raise HTTPException(status_code=401, detail="Token invalide")
return await call_next(request)
# --- Base de données en mémoire ---
tasks_db: dict[str, dict] = {}
# --- Serveur MCP ---
mcp = FastMCP("Task Manager")
@mcp.tool()
def create_task(title: str, description: str = "", priority: str = "medium") -> str:
"""Crée une nouvelle tâche dans le gestionnaire.
Args:
title: Titre de la tâche (obligatoire, max 200 caractères)
description: Description détaillée (optionnel)
priority: Niveau de priorité : low, medium, high (défaut: medium)
Returns:
JSON avec l'identifiant et les détails de la tâche créée
"""
task_id = str(uuid.uuid4())[:8]
task = {
"id": task_id,
"title": title[:200],
"description": description,
"priority": priority if priority in ("low", "medium", "high") else "medium",
"status": "open",
"created_at": datetime.now().isoformat(),
}
tasks_db[task_id] = task
return json.dumps({"success": True, "task": task}, ensure_ascii=False)
@mcp.tool()
def list_tasks(status: str = "all", priority: str = "all") -> str:
"""Liste les tâches avec filtres optionnels.
Args:
status: Filtrer par statut : all, open, completed (défaut: all)
priority: Filtrer par priorité : all, low, medium, high (défaut: all)
Returns:
JSON avec la liste des tâches et le nombre total
"""
filtered = list(tasks_db.values())
if status != "all":
filtered = [t for t in filtered if t["status"] == status]
if priority != "all":
filtered = [t for t in filtered if t["priority"] == priority]
return json.dumps({
"total": len(filtered),
"tasks": filtered,
}, ensure_ascii=False)
@mcp.tool()
def complete_task(task_id: str) -> str:
"""Marque une tâche comme terminée.
Args:
task_id: L'identifiant unique de la tâche
Returns:
Confirmation de la mise à jour ou message d'erreur
"""
if task_id not in tasks_db:
return json.dumps({"error": f"Tâche {task_id} introuvable"})
tasks_db[task_id]["status"] = "completed"
tasks_db[task_id]["completed_at"] = datetime.now().isoformat()
return json.dumps({"success": True, "task": tasks_db[task_id]}, ensure_ascii=False)
@mcp.tool()
def delete_task(task_id: str) -> str:
"""Supprime une tâche du gestionnaire.
Args:
task_id: L'identifiant unique de la tâche à supprimer
Returns:
Confirmation de la suppression ou message d'erreur
"""
if task_id not in tasks_db:
return json.dumps({"error": f"Tâche {task_id} introuvable"})
deleted = tasks_db.pop(task_id)
return json.dumps({"success": True, "deleted_task": deleted["title"]}, ensure_ascii=False)
# --- Monter le serveur MCP ---
mcp.mount(api, path="/tasks")
if __name__ == "__main__":
import uvicorn
uvicorn.run(api, host="0.0.0.0", port=8000)
Lancer et exposer le serveur
# Lancer le serveur
python task_server.py
# Dans un autre terminal, exposer via ngrok
ngrok http 8000
# URL publique : https://xyz789.ngrok-free.app
L’endpoint MCP est accessible à : https://xyz789.ngrok-free.app/tasks/mcp
Le client : client_tasks.py
# client_tasks.py
"""Client MCP pour le serveur de tâches distant avec authentification."""
import asyncio
import os
from mistralai import Mistral
from mistralai.extra.run.context import RunContext
from mistralai.extra.mcp.sse import MCPClientSSE, SSEServerParams
API_TOKEN = "mcp-secret-token-2026"
SERVER_URL = "https://xyz789.ngrok-free.app/tasks/mcp"
async def main():
client = Mistral(api_key=os.environ["MISTRAL_API_KEY"])
# Créer l'agent avec des instructions adaptées
agent = client.beta.agents.create(
model="mistral-medium-latest",
name="task-manager",
instructions=(
"Vous êtes un gestionnaire de tâches intelligent. "
"Utilisez les outils disponibles pour créer, lister et gérer "
"les tâches de l'utilisateur. Répondez en français. "
"Confirmez chaque action effectuée."
),
)
# Configurer le client MCP distant avec authentification
mcp_client = MCPClientSSE(
sse_params=SSEServerParams(
url=SERVER_URL,
headers={"Authorization": f"Bearer {API_TOKEN}"},
timeout=30,
)
)
async with RunContext(
agent_id=agent.id,
continue_on_fn_error=True,
) as run_ctx:
await run_ctx.register_mcp_client(mcp_client=mcp_client)
# Conversation avec le gestionnaire
conversations = [
"Crée une tâche haute priorité : Préparer la présentation MCP",
"Crée une tâche moyenne priorité : Relire la documentation",
"Liste toutes les tâches en cours",
"Marque la première tâche comme terminée",
"Montre-moi les tâches restantes",
]
for msg in conversations:
print(f"\n> {msg}")
result = await client.beta.conversations.run_async(
run_ctx=run_ctx,
inputs=msg,
)
print(f"Agent : {result.output}")
asyncio.run(main())
Tester dans Le Chat de Mistral
Vous pouvez aussi ajouter ce serveur directement dans Le Chat :
- Aller dans l’onglet Connectors de Le Chat
- Cliquer Add Custom Connector
- Remplir les champs :
- Name : Task Manager
- URL :
https://xyz789.ngrok-free.app/tasks/mcp - Auth : Token, coller
mcp-secret-token-2026
- Cliquer Connect
Le Chat découvre automatiquement les 4 tools et vous pouvez interagir en langage naturel.
Architecture du projet
task-mcp-project/
├── task_server.py # Serveur MCP + FastAPI + auth
├── client_tasks.py # Client Python avec agent
├── requirements.txt # fastapi, uvicorn, mcp, fastmcp, mistralai
└── .env # MISTRAL_API_KEY, API_TOKEN
De ngrok à la production
ngrok est parfait pour le développement, mais pour la production, vous aurez besoin d’un hébergement permanent. La leçon 12 couvre les options de déploiement (Docker, cloud, Hugging Face Spaces).
Points clés à retenir
- Un serveur distant combine FastAPI (HTTP) + FastMCP (MCP) + middleware (auth)
- L’authentification par token se fait via le header
Authorization: Bearer ... - Le client
MCPClientSSEaccepte des headers personnalisés pour l’authentification - Le même serveur est utilisable depuis un SDK Python ou Le Chat de Mistral
- ngrok permet de prototyper rapidement avant un déploiement production