MCP Python · FastMCP · Infra Linux
MCP Python : créer un serveur FastMCP utile pour une infra Linux
Un serveur MCP Python devient vraiment intéressant quand il expose des opérations sûres à un assistant IA : lire l’état disque, vérifier un service, consulter un extrait de logs, récupérer une ressource d’inventaire. Avec FastMCP Python, on peut construire ce pont rapidement, sans transformer l’agent en shell root incontrôlé.

Un serveur mcp python doit exposer des capacités limitées, explicites, auditables. Pas un terminal générique.
Les fastmcp tools déclenchent des actions. Les fastmcp resources publient du contexte lisible par l’agent.
Avec le transport stdio, les logs applicatifs partent sur stderr. stdout reste réservé aux messages MCP.
Pourquoi créer un MCP Python pour l’infra Linux ?
Le Model Context Protocol standardise la façon dont un assistant IA accède à des outils, ressources et prompts externes. En pratique, un python mcp server sert d’interface contrôlée entre l’assistant et ton environnement.
Pour une équipe infra, les cas utiles sont simples :
- diagnostic read-only : espace disque, uptime, statut d’un service ;
- accès cadré à des extraits de logs non sensibles ;
- exposition de ressources : inventaire, runbook, politique de maintenance ;
- préparation de commandes ou rapports, sans exécution destructive.
L’enjeu n’est pas de “donner SSH à l’IA”. L’enjeu est de publier des primitives propres : petites, nommées, testées, journalisées.
FastMCP tools vs resources : choisir le bon niveau d’accès
FastMCP simplifie l’écriture d’un serveur model context protocol python. Les décorateurs rendent le code lisible : une fonction Python devient un tool, une route de ressource devient une resource.
| Élément MCP | Usage infra | Risque |
|---|---|---|
@mcp.tool() |
Vérifier systemctl is-active nginx, lire df, retourner un extrait borné de logs. |
Plus élevé : le tool exécute du code. Allowlist stricte obligatoire. |
@mcp.resource() |
Publier un inventaire, un runbook, une matrice de services, une convention de nommage. | Plus faible, mais attention aux secrets dans les fichiers exposés. |
Diagramme de flux : de l’assistant IA au serveur Linux
Si tu veux raccorder ce serveur à tes outils de dev, le guide Claude Code, Cursor, Codex et MCP pour l’infra Linux détaille les différences de configuration côté client. Pour les accès cloud, garde le même raisonnement de moindre privilège que dans l’article AWS MCP : sécuriser l’accès API AWS.
Mini-lab testable : serveur FastMCP read-only pour Linux
Objectif : créer un serveur fastmcp python qui expose trois capacités sûres :
disk_usage(): état des filesystems viadf;service_status(name): statut d’un service autorisé ;linux_runbook: ressource texte statique pour orienter l’assistant.
Prérequis
- Debian / Ubuntu ou VM Linux de test ;
- Python 3.11+ ;
uvrecommandé pour isoler l’environnement ;- pas besoin de droits root pour le lab.
1. Créer le projet
mkdir -p ~/labs/mcp-python-infra
cd ~/labs/mcp-python-infra
uv init --bare
uv add "mcp[cli]"
Le package mcp correspond au MCP Python SDK officiel. Il inclut l’API FastMCP utilisée ci-dessous.
2. Ajouter le serveur FastMCP
# server.py
from __future__ import annotations
import logging
import subprocess
import sys
from typing import Literal
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("linux-readonly")
# Important avec le transport stdio : stdout appartient au protocole MCP.
# Les logs applicatifs doivent aller sur stderr.
logging.basicConfig(
stream=sys.stderr,
level=logging.INFO,
format="%(asctime)s %(levelname)s %(message)s",
)
logger = logging.getLogger("linux-readonly-mcp")
ALLOWED_SERVICES = {"ssh", "nginx", "docker", "cron", "fail2ban"}
def run_readonly(command: list[str], timeout: int = 5) -> str:
"""Run an allowlisted read-only command without shell=True."""
logger.info("running readonly command: %s", " ".join(command))
completed = subprocess.run(
command,
check=False,
capture_output=True,
text=True,
timeout=timeout,
)
output = completed.stdout.strip() or completed.stderr.strip()
if completed.returncode != 0:
return f"Command returned {completed.returncode}: {output[:2000]}"
return output[:4000]
@mcp.tool()
def disk_usage() -> str:
"""Return filesystem usage with human-readable values."""
return run_readonly(["df", "-h", "--output=source,fstype,size,used,avail,pcent,target"])
@mcp.tool()
def service_status(name: str) -> str:
"""Return systemd status for an allowed service name."""
if name not in ALLOWED_SERVICES:
allowed = ", ".join(sorted(ALLOWED_SERVICES))
return f"Service not allowed. Allowed values: {allowed}"
active = run_readonly(["systemctl", "is-active", name])
enabled = run_readonly(["systemctl", "is-enabled", name])
return f"service={name}\nactive={active}\nenabled={enabled}"
@mcp.tool()
def recent_service_logs(name: str, lines: int = 40) -> str:
"""Return recent journalctl lines for an allowed service, capped to 100 lines."""
if name not in ALLOWED_SERVICES:
allowed = ", ".join(sorted(ALLOWED_SERVICES))
return f"Service not allowed. Allowed values: {allowed}"
safe_lines = max(1, min(lines, 100))
return run_readonly([
"journalctl",
"-u",
name,
"-n",
str(safe_lines),
"--no-pager",
"--output=short-iso",
])
@mcp.resource("infra://runbook/linux-readonly")
def linux_runbook() -> str:
"""Expose a small operational runbook as an MCP resource."""
return """
Linux read-only runbook
=======================
Rules:
- Never execute destructive commands from this MCP server.
- Prefer disk_usage before proposing cleanup.
- Use service_status before reading service logs.
- If logs mention credentials, redact them in the final answer.
- Escalate to a human before restart, package upgrade, rm, prune, reboot.
""".strip()
if __name__ == "__main__":
mcp.run()
3. Tester localement
uv run python server.py
Le processus attend des messages MCP sur stdin. C’est normal. Interromps avec Ctrl+C. Pour une inspection plus confortable, utilise l’inspecteur MCP si disponible dans ton environnement :
uv run mcp dev server.py
shell=True.
Configuration serveur FastMCP read-only côté client
La forme exacte dépend du client MCP, mais le principe reste le même : lancer le serveur Python en stdio, avec un répertoire de travail explicite.
{
"mcpServers": {
"linux-readonly": {
"command": "uv",
"args": ["run", "python", "server.py"],
"cwd": "/home/karim/labs/mcp-python-infra"
}
}
}
Adapte cwd au chemin réel. Évite les variables d’environnement sensibles dans cette configuration. Si le serveur a besoin d’un secret, préfère un compte dédié, un token à portée minimale et une rotation claire.
Vérification, rollback et erreurs fréquentes
Checklist de vérification
- Le client voit le serveur
linux-readonly. - Les tools
disk_usage,service_statusetrecent_service_logssont listés. service_status("nginx")retourne un statut ou un refus propre.- Les logs Python apparaissent sur
stderr, pas dans les réponses MCP. - Un service non autorisé retourne Service not allowed.
Rollback
# 1. Retirer le serveur de la configuration du client MCP
# 2. Fermer/recharger le client
# 3. Supprimer le lab si nécessaire
rm -rf ~/labs/mcp-python-infra
En production, ne supprime pas le répertoire si tu veux conserver les logs, la configuration ou l’historique Git. Archive d’abord.
Erreurs fréquentes
| Symptôme | Cause probable | Correction |
|---|---|---|
| Le client MCP ne démarre pas le serveur | uv introuvable ou cwd incorrect |
Utiliser un chemin absolu vers uv ou corriger le répertoire de travail. |
| Réponses JSON-RPC corrompues | Logs envoyés sur stdout |
Configurer logging vers sys.stderr. |
journalctl ne retourne rien |
Droits utilisateur insuffisants ou unité inexistante | Tester localement avec le même utilisateur. Ajouter uniquement les droits nécessaires. |
| Le modèle demande une action destructive | Tool trop permissif ou prompt système faible | Garder le serveur read-only, refuser restart/rm/prune/reboot, demander validation humaine. |
Durcir ce modèle pour une vraie infra
Avant de brancher un serveur mcp python à une infra client, je recommande :
- compte système dédié, sans shell interactif si possible ;
- allowlist de services et chemins ;
- timeouts courts et sorties bornées ;
- redaction de secrets dans les logs et réponses ;
- tests unitaires sur chaque tool ;
- revue des droits
sudoers, idéalement aucun droit d’écriture au départ ; - journalisation côté serveur, mais jamais sur
stdouten mode stdio.
Le MCP n’annule pas les règles SysOps classiques. Il rend surtout plus important le design d’interface : ce que le serveur expose devient ce que l’assistant peut demander.
FAQ MCP Python et FastMCP
Quelle différence entre MCP Python SDK et FastMCP ?
Un serveur FastMCP Python peut-il administrer Linux ?
Pourquoi envoyer les logs sur stderr ?
Faut-il utiliser des tools ou des resources pour l’inventaire ?
Comment éviter les risques avec journalctl ?
Besoin d’un MCP propre pour ton infra Linux ?
Linux-Man peut t’aider à cadrer un serveur MCP utile : tools read-only, droits minimaux, intégration Claude/Cursor/Codex, tests, runbooks et garde-fous sécurité.
Contacte Linux-Man pour concevoir une intégration MCP adaptée à ton environnement DevOps/SysOps.

