---
title: Le serveur MCP officiel de MuleSoft ne lit pas vos logs, alors j’en ai écrit un qui le fait
description: mulewatch est un serveur MCP open source en lecture seule qui permet à Claude, Cursor ou tout client MCP de lire les logs MuleSoft Anypoint, en direct et archivés. L’astuce : trouver le bon réplica dans Monitoring Archive en un seul appel au lieu de plusieurs centaines.
date: 2026-09-18
tags: MuleSoft, Anypoint Platform, MCP, observabilité
project: https://github.com/brbousnguar/mulewatch
---

MuleSoft a un serveur MCP officiel. Il crée des squelettes de projet, génère des flows, déploie des apps. Ce qu’il ne sait pas faire, c’est la seule chose dont j’ai besoin quand ça casse en prod : lire les logs.

Alors j’ai écrit [mulewatch](https://github.com/brbousnguar/mulewatch). C’est un serveur MCP open source qui ouvre vos logs Anypoint, en direct et archivés, à Claude, Cursor ou n’importe quel client MCP. En lecture seule. Une ligne pour le lancer : `npx -y mulewatch`.

J’en ai d’abord écrit une version pour mon travail. Celle-ci, c’est la version nettoyée, qui marche chez tout le monde.

## Construire ou exploiter

Le [serveur officiel](https://docs.mulesoft.com/mulesoft-mcp-server) sert à construire des apps Mule, et il le fait bien. Mais il attend un projet Mule local ouvert dans votre IDE. Quand un flow plante en prod, vous n’en avez en général aucun d’ouvert. Vous avez Runtime Manager dans un onglet du navigateur.

mulewatch couvre l’autre moitié du travail : comprendre ce qui s’est passé. Logs, apps déployées, instances API Manager, assets Exchange. Rien qui écrive.

<figure class="fd-fig mw-split-fig" aria-labelledby="mw1-cap" data-desc="Deux serveurs MCP pour les deux moitiés du travail. Le serveur MCP officiel de MuleSoft sert à construire : il crée des squelettes de projet, génère des flows et déploie des apps, et il attend un projet Mule local ouvert dans votre IDE. mulewatch sert à exploiter : quand un flow plante en prod, vous avez Runtime Manager dans un onglet du navigateur, et mulewatch lit les logs, les apps déployées, les instances API Manager et les assets Exchange. Rien, dans mulewatch, n’écrit.">
<div class="mp-two-row">
<div class="mp-two-card">
<p class="mp-two-who">Construire</p>
<p class="mp-two-name">Serveur officiel</p>
<svg class="mp-two-art" viewBox="0 0 300 96" aria-hidden="true">
<rect class="mp-w" x="4" y="4" width="292" height="88"/><rect class="mp-wbar" x="4" y="4" width="292" height="9"/>
<rect class="mp-wside" x="4" y="13" width="62" height="79"/>
<rect class="mp-ln" x="12" y="22" width="40" height="3"/><rect class="mp-ln" x="20" y="31" width="36" height="3"/><rect class="mp-ln" x="20" y="40" width="30" height="3"/><rect class="mp-ln" x="12" y="49" width="44" height="3"/><rect class="mp-ln" x="20" y="58" width="32" height="3"/>
<path class="fd-line" d="M128,52 H150 M200,52 H222"/>
<rect class="hx-drw" x="84" y="40" width="44" height="24"/><rect class="hx-drw" x="150" y="40" width="50" height="24"/><rect class="hx-drw" x="222" y="40" width="44" height="24"/>
<text class="mp-art-t" x="175" y="82" text-anchor="middle">projet Mule local</text>
</svg>
<p class="mp-two-line">Crée des projets, génère des flows, déploie des apps</p>
<p class="mp-two-line">Attend un projet Mule local ouvert dans l’IDE</p>
</div>
<p class="mp-two-vs">vs</p>
<div class="mp-two-card mp-two-peter">
<p class="mp-two-who">Exploiter</p>
<p class="mp-two-name">mulewatch</p>
<svg class="mp-two-art" viewBox="0 0 300 96" aria-hidden="true">
<rect class="mp-w" x="4" y="4" width="292" height="88"/><rect class="mp-wbar" x="4" y="4" width="292" height="9"/>
<rect class="mp-ln" x="14" y="21" width="230" height="4"/><rect class="mp-ln" x="14" y="31" width="190" height="4"/><rect class="mw-err" x="14" y="41" width="212" height="4"/><rect class="mp-ln" x="14" y="51" width="240" height="4"/><rect class="mp-ln" x="14" y="61" width="170" height="4"/><rect class="mp-ln" x="14" y="71" width="220" height="4"/>
<rect class="mw-read" x="8" y="17" width="284" height="12"/>
<text class="mp-art-t" x="282" y="88" text-anchor="end">onglet Runtime Manager</text>
</svg>
<p class="mp-two-line">Logs, apps déployées, API Manager, Exchange</p>
<p class="mp-two-line">Rien qui écrive</p>
</div>
</div>
<figcaption id="mw1-cap">Deux moitiés du même travail. Le serveur officiel vous aide à construire l’app ; mulewatch lit ce qui s’est passé une fois qu’elle tourne. La ligne rouge, c’est celle que vous cherchez.</figcaption>
</figure>

## Les logs en direct oublient vite

Les logs que vous voyez dans Runtime Manager sont un live tail : un petit tampon circulaire par app. Sur une app chargée, une ligne en sort en quelques minutes, quel que soit le `startTime` que vous passez. Si le problème date d’hier soir, il a déjà disparu.

Le vrai historique se trouve dans l’API Anypoint Monitoring Archive. Elle range les logs dans des fichiers de 10 minutes, qui arrivent environ 10 minutes après la fin de chaque fenêtre, et les garde longtemps. Il faut qu’Anypoint Monitoring soit activé sur l’org.

<figure class="fd-fig mw-time-fig" aria-labelledby="mw2-cap" data-desc="Un schéma de l’endroit où se trouvent les logs, pas à l’échelle. Le temps va de gauche à droite, d’hier soir à maintenant. Les logs en direct de Runtime Manager sont un petit tampon circulaire par app : sur une app chargée, ils ne couvrent que les dernières minutes, donc un problème d’hier soir en a déjà disparu, quel que soit le startTime passé. L’Anypoint Monitoring Archive garde tout l’historique dans des fichiers de 10 minutes, chacun arrivant environ 10 minutes après la fin de sa fenêtre, et les conserve longtemps : le problème d’hier soir y est toujours. L’archive demande qu’Anypoint Monitoring soit activé sur l’org.">
<div class="mw-time">
<div class="mw-lane">
<p class="mw-lane-name">Logs en direct<span>un petit tampon circulaire par app</span></p>
<div class="mw-track-w"><p class="mw-track"><i class="mw-tail"></i><b class="mw-hit mw-hit-gone"></b></p></div>
</div>
<div class="mw-lane">
<p class="mw-lane-name">Monitoring Archive<span>fichiers de 10 minutes, gardés longtemps</span></p>
<div class="mw-track-w"><p class="mw-track mw-files"><i class="mw-new"></i><b class="mw-hit mw-hit-found"></b></p>
<p class="mw-axis"><span class="mw-axis-then">hier soir</span><span class="mw-axis-now">maintenant</span></p></div>
</div>
</div>
<p class="mp-strip"><b>Prérequis</b><span>Anypoint Monitoring activé sur l’org.</span></p>
<figcaption id="mw2-cap">Un schéma, pas à l’échelle. La marque rouge, c’est le problème d’hier soir : disparu des logs en direct, toujours dans l’archive. Le fichier en pointillés au bout, c’est la fenêtre qui n’est pas encore arrivée.</figcaption>
</figure>

## Le plus amusant : trouver le bon réplica

Voilà le piège. L’archive n’est pas indexée par app. Elle est indexée par réplica, sous la forme `{appName}_{replicaId}`, et chaque redéploiement crée de nouveaux réplicas. Impossible, donc, de demander simplement « les logs d’order-sync-api du 9 août ». Il faut savoir quel pod tournait ce jour-là.

L’app sur laquelle j’ai testé comptait **669** entités de réplica dans l’archive. L’API autorise 60 requêtes par minute. Les sonder toutes, c’est environ 11 minutes d’attente avant de lire la moindre ligne.

En fait, on n’en a presque jamais besoin. Un pod vit en général jusqu’au redéploiement suivant, donc le réplica qui tourne en ce moment a sans doute aussi écrit les logs récents. Voici ce que fait mulewatch :

1. Récupérer les ID de réplica dans les logs en direct et les essayer en premier.
2. Aucun fichier pour cette date ? L’app a été redéployée depuis. Se replier sur un scan de tous les réplicas qu’elle a eus, plafonné par `maxEntities`.

Sur ce test, une recherche sur la date du jour a sondé **1** réplica, trouvé 127 fichiers d’archive et analysé 2 859 lignes de log. Une date antérieure au dernier redéploiement est retombée sur le scan complet, comme il se doit. Le client reste aussi sous la limite de débit et temporise quand il reçoit un `429` : une longue recherche ralentit au lieu d’échouer.

<figure class="fd-fig mw-rep-fig" aria-labelledby="mw3-cap" data-desc="Trouver le bon réplica, avec les chiffres de l’app de la note. L’archive est indexée par réplica, sous la forme du nom de l’app et d’un id de réplica, et chaque redéploiement crée de nouveaux réplicas. L’app de test comptait 669 entités de réplica. Les sonder toutes à 60 requêtes par minute, c’est environ 11 minutes d’attente avant de lire la moindre ligne. mulewatch essaie d’abord les ID de réplica vus dans les logs en direct, puisqu’un pod vit en général jusqu’au redéploiement suivant : une recherche sur la date du jour a sondé 1 réplica, trouvé 127 fichiers d’archive et analysé 2 859 lignes de log. Quand il n’y a aucun fichier pour cette date, l’app a été redéployée depuis, et mulewatch se replie sur un scan de tous les réplicas qu’elle a eus, plafonné par maxEntities.">
<div class="mp-two-row">
<div class="mp-two-card">
<p class="mp-two-who">Sonder chaque réplica</p>
<p class="mp-two-name">~11 min</p>
<svg class="mp-two-art" viewBox="0 0 300 98" aria-hidden="true">
<defs><pattern id="mw3-a" width="6.5" height="6.5" patternUnits="userSpaceOnUse"><rect class="mw-cell" width="5" height="5"/></pattern></defs>
<rect fill="url(#mw3-a)" x="0" y="0" width="299" height="91"/><rect fill="url(#mw3-a)" x="0" y="91" width="162.5" height="6.5"/>
<rect class="mw-rowscan" x="-1" y="-0.75" width="300" height="6.5"/>
</svg>
<p class="mp-two-line">669 réplicas, indexés en <code>{appName}_{replicaId}</code></p>
<p class="mp-two-line">60 requêtes par minute</p>
<p class="mp-two-line">L’attente avant la première ligne</p>
</div>
<p class="mp-two-vs">vs</p>
<div class="mp-two-card mp-two-peter">
<p class="mp-two-who">Logs en direct d’abord</p>
<p class="mp-two-name">1 réplica sondé</p>
<svg class="mp-two-art" viewBox="0 0 300 98" aria-hidden="true">
<defs><pattern id="mw3-b" width="6.5" height="6.5" patternUnits="userSpaceOnUse"><rect class="mw-cell" width="5" height="5"/></pattern></defs>
<rect fill="url(#mw3-b)" x="0" y="0" width="299" height="91"/><rect fill="url(#mw3-b)" x="0" y="91" width="162.5" height="6.5"/>
<rect class="mw-live" x="156" y="91" width="5" height="5"/><rect class="mw-ring" x="152" y="87" width="13" height="13"/>
</svg>
<p class="mp-two-line">Le réplica actuel, vu dans les logs en direct</p>
<p class="mp-two-line">127 fichiers d’archive trouvés</p>
<p class="mp-two-line">2 859 lignes de log analysées</p>
</div>
</div>
<p class="mp-strip"><b>Redéployée depuis</b><span>Aucun fichier pour cette date : scan de tous les réplicas de l’app, plafonné par <code>maxEntities</code>.</span></p>
<figcaption id="mw3-cap">Un carré par réplica, les 669, de l’app sur laquelle j’ai testé. À gauche, le balayage montre ce que coûte un sondage complet à la limite de débit ; à droite, le seul carré qui a répondu pour aujourd’hui.</figcaption>
</figure>

## En lecture seule, exprès

Je ne veux pas qu’un LLM redémarre des apps Mule en production. Il n’y a donc dans mulewatch aucun outil pour déployer, arrêter, redémarrer ou appliquer une policy. Aucun. Si vous en avez besoin, utilisez le serveur officiel à côté.

Vous pouvez aussi restreindre tout le serveur aux environnements de confiance :

```dotenv
ANYPOINT_ALLOWED_ENVIRONMENTS=Dev,Sandbox
```

Tout ce qui n’est pas dans la liste est refusé, raison à l’appui. Couplez ce réglage à une connected app qui ne voit que ces environnements, et la prod est deux fois hors d’atteinte.

<figure class="fd-fig mw-ro-fig" aria-labelledby="mw4-cap" data-desc="La lecture seule, de deux façons. D’abord les outils : mulewatch lit les logs, les apps déployées, les instances API Manager et les assets Exchange, et n’a aucun outil pour déployer, arrêter, redémarrer ou appliquer une policy. Ensuite les environnements : avec ANYPOINT_ALLOWED_ENVIRONMENTS réglé sur Dev et Sandbox, les appels vers ces deux-là passent, et tout ce qui n’est pas dans la liste, comme Prod, est refusé, raison à l’appui. Couplez ce réglage à une connected app qui ne voit que ces environnements, et la prod est deux fois hors d’atteinte.">
<div class="mw-ro">
<div class="mw-tools">
<p class="mw-tools-h">Lit</p>
<ul class="mw-chips"><li>logs</li><li>apps déployées</li><li>API Manager</li><li>Exchange</li></ul>
<p class="mw-tools-h">Écrit</p>
<ul class="mw-chips mw-chips-none"><li>déployer</li><li>arrêter</li><li>redémarrer</li><li>policy</li></ul>
</div>
<p class="hx-meta"><b>Allow-list</b><span class="mw-var">ANYPOINT_ALLOWED_ENVIRONMENTS<wbr>=Dev,Sandbox</span></p>
<ul class="hx-lanes">
<li class="hx-lane"><p class="hx-lane-name">Dev<span>dans la liste</span></p><p class="hx-run"><i></i></p><p class="hx-score">lu</p></li>
<li class="hx-lane"><p class="hx-lane-name">Sandbox<span>dans la liste</span></p><p class="hx-run"><i></i></p><p class="hx-score">lu</p></li>
<li class="hx-lane mw-lane-no"><p class="hx-lane-name">Prod<span>hors de la liste</span></p><p class="mw-gates"><i>1 allow-list</i><i>2 connected app</i></p><p class="mw-refused">refusé</p></li>
</ul>
</div>
<p class="mp-strip"><b>Pour déployer ou redémarrer</b><span>Utilisez le serveur officiel à côté.</span></p>
<figcaption id="mw4-cap">Aucun outil qui écrit, et une allow-list qui refuse tout ce qui n’y figure pas, avec la raison. La deuxième barrière devant Prod, c’est une connected app qui ne voit que Dev et Sandbox.</figcaption>
</figure>

## Le piège qui vous attend

Les environnements sont rattachés aux business groups, pas à l’org racine. Les identifiants d’une connected app pointent sur l’org racine, qui n’a souvent aucun environnement. Votre premier appel revient donc vide et vous croyez vos identifiants invalides. Ils ne le sont pas.

Appelez `anypoint_list_business_groups`, récupérez le bon id, renseignez `ANYPOINT_ORG_ID`. C’est réglé.

Même logique pour les scopes : Anypoint répond à un scope manquant par un simple `403`, sans explication. mulewatch l’intercepte et vous dit quel scope il vous manque sans doute.

<figure class="fd-fig mw-org-fig" aria-labelledby="mw5-cap" data-desc="Le piège en trois étapes. Un : les identifiants d’une connected app pointent sur l’org racine, qui n’a souvent aucun environnement, donc le premier appel revient vide et les identifiants semblent invalides. Ils ne le sont pas : les environnements sont rattachés aux business groups. Deux : listez les business groups avec anypoint_list_business_groups et récupérez le bon id. Trois : renseignez ANYPOINT_ORG_ID avec cet id, et les environnements apparaissent. À part : Anypoint répond à un scope manquant par un simple 403 ; mulewatch l’intercepte et dit quel scope il vous manque sans doute.">
<ol class="mp-trio">
<li class="mp-trio-step">
<svg class="mp-grow-art" viewBox="0 0 120 110" aria-hidden="true">
<path class="hx-dim" d="M60,34 V48 M30,48 H90 M30,48 V60 M90,48 V60"/>
<rect class="mw-root-off" x="34" y="8" width="52" height="26"/><text class="mw-zero" x="60" y="26" text-anchor="middle">0</text>
<rect class="hx-drw" x="12" y="60" width="36" height="20"/><rect class="hx-drw" x="72" y="60" width="36" height="20"/>
<rect class="mw-env" x="14" y="88" width="9" height="9"/><rect class="mw-env" x="25.5" y="88" width="9" height="9"/><rect class="mw-env" x="37" y="88" width="9" height="9"/>
<rect class="mw-env" x="74" y="88" width="9" height="9"/><rect class="mw-env" x="85.5" y="88" width="9" height="9"/><rect class="mw-env" x="97" y="88" width="9" height="9"/>
<rect class="mw-key" x="80" y="4" width="12" height="12"/>
</svg>
<p class="mp-trio-n"><b>1</b>Premier appel vide</p>
<p class="mp-trio-what">Les identifiants tombent sur l’org racine, souvent sans aucun environnement</p>
</li>
<li class="mp-trio-step">
<svg class="mp-grow-art" viewBox="0 0 120 110" aria-hidden="true">
<rect class="hx-drw" x="14" y="10" width="92" height="90"/>
<path class="hx-fl" d="M14,32 H106 M14,54 H106 M14,76 H106"/>
<rect class="mp-ln" x="24" y="19" width="60" height="4"/><rect class="mp-ln" x="24" y="41" width="50" height="4"/><rect class="mp-ln" x="24" y="63" width="64" height="4"/><rect class="mp-ln" x="24" y="85" width="44" height="4"/>
<rect class="mw-pick" x="16" y="56" width="88" height="18"/>
</svg>
<p class="mp-trio-n"><b>2</b>Lister les groupes</p>
<p class="mp-trio-what">Les environnements sont rattachés aux business groups. Prenez le bon id</p>
</li>
<li class="mp-trio-step">
<svg class="mp-grow-art" viewBox="0 0 120 110" aria-hidden="true">
<path class="hx-dim" d="M60,34 V48 M30,48 H90 M30,48 V60 M90,48 V60"/>
<rect class="hx-drw" x="34" y="8" width="52" height="26"/>
<rect class="hx-drw" x="12" y="60" width="36" height="20"/><rect class="mw-bg-on" x="72" y="60" width="36" height="20"/>
<rect class="mw-env" x="14" y="88" width="9" height="9"/><rect class="mw-env" x="25.5" y="88" width="9" height="9"/><rect class="mw-env" x="37" y="88" width="9" height="9"/>
<rect class="mw-env-on" x="74" y="88" width="9" height="9"/><rect class="mw-env-on" x="85.5" y="88" width="9" height="9"/><rect class="mw-env-on" x="97" y="88" width="9" height="9"/>
<rect class="mw-key" x="102" y="54" width="12" height="12"/>
</svg>
<p class="mp-trio-n"><b>3</b>Renseigner l’org id</p>
<p class="mp-trio-what">Pointez-le sur ce groupe et les environnements apparaissent</p>
</li>
</ol>
<p class="mp-strip mw-strip"><b>Le correctif</b><span>Appelez <code>anypoint_list_business_groups</code>, puis renseignez <code>ANYPOINT_ORG_ID</code>.</span></p>
<p class="mp-strip mw-strip"><b>Un 403 sans explication</b><span>mulewatch l’intercepte et nomme le scope qui vous manque sans doute.</span></p>
<figcaption id="mw5-cap">Le carré jaune, c’est là que pointent vos identifiants. À gauche, l’org racine, sans rien à lire en dessous ; à droite, le business group qui porte les environnements.</figcaption>
</figure>

## Essayez-le

Créez une connected app (« acts on its own behalf »), donnez-lui des scopes en lecture, et ajoutez ceci à Claude Desktop, Cursor ou OpenClaw :

```json
{
  "mcpServers": {
    "mulewatch": {
      "command": "npx",
      "args": ["-y", "mulewatch"],
      "env": {
        "ANYPOINT_CLIENT_ID": "your_connected_app_client_id",
        "ANYPOINT_CLIENT_SECRET": "your_connected_app_client_secret"
      }
    }
  }
}
```

Ou dans Claude Code :

```bash
claude mcp add mulewatch --env ANYPOINT_CLIENT_ID=... --env ANYPOINT_CLIENT_SECRET=... -- npx -y mulewatch
```

Lancez d’abord `anypoint_whoami` pour voir à quoi il est connecté. Ensuite, demandez simplement : *« montre-moi les lignes ERROR d’order-sync-api en Prod hier entre 1 h et 3 h du matin »*

<figure class="fd-fig mp-loop mw-try-fig" aria-labelledby="mw6-cap" data-desc="Démarrer en trois étapes. Un : dans Anypoint, créez une connected app qui agit en son propre nom et donnez-lui des scopes en lecture. Deux : ajoutez mulewatch à votre client MCP, Claude Desktop, Cursor ou OpenClaw, ou avec claude mcp add dans Claude Code ; il se lance avec npx -y mulewatch. Trois : lancez d’abord anypoint_whoami pour voir à quoi il est connecté. Ensuite, demandez en langage courant, par exemple : montre-moi les lignes ERROR d’order-sync-api en Prod hier entre 1 h et 3 h du matin.">
<div class="mp-loop-in">
<div class="flow" role="group" aria-label="Démarrer : créer une connected app avec des scopes en lecture, ajouter mulewatch à votre client MCP, puis lancer d’abord anypoint_whoami">
  <div class="flow-node">
    <p class="flow-label">1 · Anypoint</p>
    <h3>Connected app</h3>
    <ul>
      <li>Agit en son propre nom</li>
      <li>Scopes en lecture</li>
    </ul>
  </div>
  <div class="flow-link" aria-hidden="true"><span>→</span></div>
  <div class="flow-node flow-node--core">
    <p class="flow-label">2 · Votre client</p>
    <h3>npx -y mulewatch</h3>
    <ul>
      <li>Claude Desktop, Cursor ou OpenClaw</li>
      <li>Ou claude mcp add dans Claude Code</li>
    </ul>
  </div>
  <div class="flow-link" aria-hidden="true"><span>→</span></div>
  <div class="flow-node">
    <p class="flow-label">3 · Premier appel</p>
    <h3>Vérifier</h3>
    <ul>
      <li class="mw-wrap">anypoint_<wbr>whoami</li>
      <li>Voir à quoi il est connecté</li>
    </ul>
  </div>
</div>
</div>
<p class="mp-strip"><b>Puis demandez</b><span>« Montre-moi les lignes ERROR d’order-sync-api en Prod hier entre 1 h et 3 h du matin. »</span></p>
<figcaption id="mw6-cap">Trois étapes jusqu’à une première réponse. C’est la v0.1, sous Apache-2.0 ; tout sauf API Manager a été testé sur une org réelle.</figcaption>
</figure>

Le projet en est à la v0.1, sous licence Apache-2.0, sur [npm](https://www.npmjs.com/package/mulewatch) et dans le [registre MCP](https://registry.modelcontextprotocol.io/?q=mulewatch). Tout est testé sur une org réelle, sauf API Manager, parce que mon app de test n’avait pas le scope `View APIs Configuration`. Si vous utilisez MuleSoft, essayez-le et dites-moi ce qui casse. Les issues et les PR sont ouvertes sur [GitHub](https://github.com/brbousnguar/mulewatch).

*MuleSoft et Anypoint Platform sont des marques de Salesforce. mulewatch est un projet indépendant.*
