Site icon

MCP Python : créer un serveur FastMCP utile pour une infra Linux

Serveur MCP Python FastMCP pour interroger une infrastructure Linux en lecture seule

FastMCP permet d’exposer des outils MCP Python contrôlés pour lire l’état d’une infrastructure Linux sans ouvrir un shell générique.

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é.

Objectif : python mcp server read-only
Stack : Python 3.11+, FastMCP, stdio
Public : DevOps / SysOps Linux
À retenir #1

Un serveur mcp python doit exposer des capacités limitées, explicites, auditables. Pas un terminal générique.

À retenir #2

Les fastmcp tools déclenchent des actions. Les fastmcp resources publient du contexte lisible par l’agent.

À retenir #3

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.
Bonne pratique : commence par des resources et des tools read-only. Ajoute des actions d’écriture uniquement après revue sécurité, gestion d’approbation humaine et traçabilité.

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 via df ;
  • 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+ ;
  • uv recommandé 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
Point sécurité : ce lab ne prend jamais une commande brute fournie par le modèle. Il accepte uniquement un nom de service dans une allowlist, puis construit une commande sans 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_status et recent_service_logs sont 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 stdout en 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 ?
Le MCP Python SDK fournit les bases officielles pour créer clients et serveurs MCP en Python. FastMCP est l’API haut niveau qui simplifie la déclaration des tools et resources avec des décorateurs Python.
Un serveur FastMCP Python peut-il administrer Linux ?
Techniquement oui, mais il faut éviter le serveur “shell universel”. Pour l’infra, commence par du read-only : statut, inventaire, logs bornés, ressources de runbook. Les actions d’écriture doivent passer par approbation humaine et droits minimaux.
Pourquoi envoyer les logs sur stderr ?
Avec le transport stdio, stdout transporte les messages du protocole MCP. Si l’application écrit des logs sur stdout, elle peut casser l’échange JSON-RPC. stderr est le bon canal pour les logs serveur.
Faut-il utiliser des tools ou des resources pour l’inventaire ?
Pour un inventaire statique ou semi-statique, une resource est souvent préférable. Un tool devient utile si l’information doit être calculée à la demande, filtrée ou récupérée depuis une commande.
Comment éviter les risques avec journalctl ?
Limiter les unités accessibles, borner le nombre de lignes, imposer un timeout, filtrer ou redacter les secrets, et tester avec le même utilisateur que celui lancé par le client MCP.

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.


Quitter la version mobile