Aller au contenu
Développement medium

Observabilité FinOps de la stack par un serveur MCP

10 min de lecture

logo python

Une stack souveraine a un coût — matériel, électricité, disque. Le fil rouge a tout assemblé ; il manque de quoi mesurer ce que la stack consomme. Cette étape ajoute ce chaînon, et le fait par le protocole MCP : un serveur MCP FinOps qui expose, comme des outils, l'inventaire des conteneurs, leur consommation de ressources, l'empreinte disque et une estimation de coût. Exposé en MCP, ce serveur devient un outil interrogeable par un agent — l'assistant du fil rouge peut alors répondre à « combien consomme la stack ? ». C'est aussi la brique MCP du fil rouge, qui relie le projet à tout le parcours MCP. Public visé : développeur qui veut donner à sa stack une visibilité FinOps, et la rendre interrogeable.

  • Pourquoi exposer l'observabilité FinOps par un serveur MCP.
  • Interroger le démon Docker depuis Python.
  • Construire des outils MCP : inventaire, ressources, disque, coût.
  • Estimer le coût d'une stack à partir de son empreinte.
  • Brancher ce serveur à un agent comme outil.

Le guide sur l'exposition de la stack a posé le bilan de coûts : une stack self-hosted a un coût fixe — matériel, électricité, disque. Mais un bilan théorique ne suffit pas : il faut mesurer ce qui tourne réellement.

D'où ce composant. Et le choix du MCP pour l'exposer n'est pas anodin. On pourrait écrire un simple script. Mais en faire un serveur MCP, c'est rendre ces données FinOps interrogeables par un agent : l'assistant du fil rouge, ou tout autre client MCP, peut appeler ces outils sans rien savoir de leur implémentation. Le FinOps en langage naturel : « quels conteneurs tournent ? », « combien coûte la stack ? ».

C'est aussi ce qui rattache le fil rouge au parcours MCP. Le protocole MCP standardise la mise à disposition d'outils ; ce serveur FinOps en est une application concrète, dans la stack souveraine.

La matière première vient du démon Docker. Le SDK Python docker s'y connecte en une ligne, et donne accès aux conteneurs, aux images, aux volumes.

import docker
_client = docker.from_env()

docker.from_env() lit la configuration de l'environnement — le socket Docker local. À partir du client, containers.list() donne les conteneurs en service, df() l'usage disque, et chaque conteneur expose ses statistiques d'exécution. C'est tout ce dont les outils FinOps ont besoin.

Le serveur expose quatre outils, chacun répondant à une question de coût précise.

L'inventaire d'abord : quels conteneurs tournent, sur quelles images.

@mcp.tool()
def inventaire_conteneurs() -> list[dict]:
"""Liste les conteneurs Docker en service : nom, image, état."""
return [
{"nom": c.name,
"image": (c.image.tags[0] if c.image.tags else c.image.short_id),
"etat": c.status}
for c in _client.containers.list()
]

La consommation de ressources ensuite : pour chaque conteneur, le CPU et la mémoire. La mémoire se lit directement dans les statistiques ; le CPU se calcule à partir d'un instantané.

@mcp.tool()
def consommation_ressources() -> list[dict]:
"""Donne, par conteneur, le CPU et la mémoire consommés."""
mesures = []
for c in _client.containers.list():
stats = c.stats(stream=False)
memoire = stats.get("memory_stats", {}).get("usage", 0)
mesures.append({"nom": c.name,
"cpu_pourcent": _cpu_pourcent(stats),
"memoire_mo": round(memoire / 1e6, 1)})
return mesures

L'empreinte disque : la place prise par les images et les volumes. Et l'estimation de coût, qui agrège le tout — on y vient.

Le quatrième outil est le plus « FinOps » : il traduit l'empreinte en euros. Le calcul lui-même est isolé dans une fonction pure, séparée de toute interrogation de Docker — donc simple à tester et à ajuster.

COUT_GO_RAM_MOIS = 0.50 # hypothèses de lab, à ajuster
COUT_GO_DISQUE_MOIS = 0.10
def estimer_cout(total_disque_go: float, memoire_totale_mo: float) -> dict:
"""Estime un coût mensuel à partir d'une empreinte — fonction pure."""
cout_ram = (memoire_totale_mo / 1024) * COUT_GO_RAM_MOIS
cout_disque = total_disque_go * COUT_GO_DISQUE_MOIS
return {"cout_ram_mensuel": round(cout_ram, 2),
"cout_disque_mensuel": round(cout_disque, 2),
"cout_total_mensuel": round(cout_ram + cout_disque, 2)}

L'outil MCP estimation_cout se contente d'agréger : il appelle l'empreinte disque, somme la mémoire des conteneurs, et passe le tout à estimer_cout.

Les quatre fonctions deviennent des outils MCP par un simple décorateur @mcp.tool() — exactement le mécanisme du guide créer un serveur MCP.

from mcp.server.fastmcp import FastMCP
mcp = FastMCP("finops")
# ... les fonctions décorées par @mcp.tool() ...
if __name__ == "__main__":
mcp.run()

La docstring de chaque outil compte autant que son code : c'est elle que le modèle lit pour décider quand l'appeler. « Liste les conteneurs Docker en service » dit à l'agent à quoi sert l'outil. Le serveur démarré, il parle le protocole MCP — prêt à être consommé.

Un serveur MCP ne vaut que branché. Le guide connecter un agent au MCP en a montré le mécanisme : un client MCP découvre les outils d'un serveur et les met à disposition de l'agent. Le fil rouge l'applique pour de bon — l'assistant documentaire consomme ce serveur FinOps.

Le pivot est un routeur. Avant tout traitement, l'assistant classe la question. Une question documentaire — « comment fonctionne un volume ? » — part vers le RAG. Une question d'infrastructure — « combien de conteneurs tournent ? » — part vers la brique FinOps.

if aiguiller(question, historique) == "finops":
prep = await preparer_finops(question) # session MCP + appels d'outils
# ... la réponse est rédigée à partir des données collectées

Sur la voie FinOps, preparer_finops ouvre une session MCP vers le serveur — lancé en sous-processus, en transport stdio —, liste ses outils, demande au modèle lesquels appeler, exécute les appels retenus et renvoie des données réelles. La réponse finale est ensuite rédigée à partir de ces chiffres, jamais devinée.

C'est la promesse du MCP appliquée au fil rouge : la stack souveraine expose ses propres données par un protocole standard, et son assistant les consulte comme n'importe quel autre outil. Le projet, RAG au départ, devient agentique au sens plein — un agent qui interroge des outils.

SymptômeCause probableSolution
permission denied sur le socket DockerUtilisateur hors du groupe dockerAjouter l'utilisateur au groupe, rouvrir la session
inventaire_conteneurs renvoie une liste videAucun conteneur en serviceDémarrer la stack du fil rouge
CPU toujours à 0 %Conteneur au repos, ou un seul instantanéNormal au repos ; la formule exige precpu_stats
Empreinte disque sous-évaluéeTailles de volumes non calculéesdocker system df force le calcul
L'agent n'appelle pas l'outilDocstring trop vagueDécrire précisément ce que fait chaque outil
  • Une stack souveraine doit mesurer ce qu'elle consomme — pas seulement l'estimer en théorie.
  • En faire un serveur MCP rend ces données interrogeables par un agent.
  • Le SDK docker donne accès aux conteneurs, aux statistiques et à l'usage disque.
  • Le pourcentage CPU se calcule à partir de deux instantanés de stats.
  • L'estimation de coût repose sur des hypothèses à ajuster — elle donne un ordre de grandeur.
  • Branché à l'assistant, ce serveur fait du fil rouge un agent qui interroge des outils.

Ce site vous est utile ?

Sachez que moins de 1% des lecteurs soutiennent ce site.

Je maintiens +700 guides gratuits, sans pub ni tracing. Aujourd'hui, ce site ne couvre même pas mes frais d'hébergement, d'électricité, de matériel, de logiciels, mais surtout de cafés.

Un soutien régulier, même symbolique, m'aide à garder ces ressources gratuites et à continuer de produire des guides de qualité. Merci pour votre appui.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn