5.5 KiB
xdebug-mcp — Design P0
Objectif
Serveur MCP en Go capable de lire et d'analyser des fichiers de sortie de profilage Xdebug (format cachegrind, compressés gzip). Exposé via stdio conformément au protocole MCP.
Stack
- Langage : Go
- Protocole MCP :
mark3labs/mcp-go - Framework CLI/config :
forge.lclr.dev/AI/mcp-framework+ package générémcpgen/ - Format d'entrée : cachegrind v1 (xdebug 3.x), fichiers
.gzou texte brut
Structure du projet
xdebug-mcp/
├── cmd/xdebug-mcp/main.go # Point d'entrée, bootstrap CLI
├── internal/
│ ├── app/app.go # Wiring MCP server
│ ├── cachegrind/
│ │ ├── parser.go # Parser streaming du format cachegrind
│ │ └── model.go # Structures de données (Profile, Function, Call)
│ ├── cache/
│ │ └── lru.go # Cache LRU keyed par chemin fichier absolu
│ └── tools/
│ ├── analyze.go # Tool analyze_profile
│ ├── callers.go # Tool get_callers
│ └── callees.go # Tool get_callees
├── mcpgen/ # Généré par `mcp-framework generate`
│ └── manifest.go
├── mcp.toml
└── go.mod
Modèle de données (cachegrind/model.go)
type Profile struct {
Cmd string
Events []string // ex: ["Time_(10ns)", "Memory_(bytes)"]
Functions []*Function
ByName map[string][]*Function // une clé peut matcher plusieurs fichiers
}
type Function struct {
Name string
File string
Costs []int64 // indexé sur Profile.Events
Calls []*Call // appels sortants (callees)
CalledBy []*Call // appels entrants (callers)
}
type Call struct {
Caller *Function
Callee *Function
Count int64
Costs []int64
}
Les coûts sont agrégés : si une fonction apparaît dans plusieurs blocs fl=/fn=, ses coûts sont sommés. CalledBy est reconstruit à partir des directives calls= pendant le parse. Le parser maintient une table de résolution interne des alias numériques (fl=(N), fn=(N)).
Tools MCP
Le chemin du fichier est toujours un paramètre direct de l'outil — pas de configuration globale de dossier.
analyze_profile
| Paramètre | Type | Défaut | Description |
|---|---|---|---|
file_path |
string | requis | Chemin absolu ou relatif du fichier cachegrind (.gz ou texte) |
top_n |
int | 20 | Nombre de fonctions à retourner |
Retourne : stats globales (commande profilée, événements, nombre total de fonctions) + top N fonctions triées par Costs[0] (Time), avec coûts absolus et pourcentage du total.
get_callers
| Paramètre | Type | Défaut | Description |
|---|---|---|---|
file_path |
string | requis | Chemin du fichier cachegrind |
function_name |
string | requis | Nom exact ou sous-chaîne de la fonction cible |
top_n |
int | 10 | Nombre de callers à retourner |
Retourne : liste des fonctions qui appellent function_name, avec nombre d'appels et coûts associés, triés par coût décroissant.
get_callees
| Paramètre | Type | Défaut | Description |
|---|---|---|---|
file_path |
string | requis | Chemin du fichier cachegrind |
function_name |
string | requis | Nom exact ou sous-chaîne de la fonction cible |
top_n |
int | 10 | Nombre de callees à retourner |
Retourne : liste des fonctions appelées par function_name, avec nombre d'appels et coûts associés, triés par coût décroissant.
Résolution du nom : recherche exacte d'abord (ByName), puis recherche contains si aucun résultat exact. Si plusieurs fonctions matchent en mode contains, toutes sont listées avec un avertissement.
Cache LRU (cache/lru.go)
- Capacité : 2 fichiers simultanés (constante
DefaultCapacity = 2) - Clé : chemin absolu du fichier
- Invalidation : comparaison de
os.FileInfo.ModTime()à chaque accès - Thread-safe :
sync.Mutex - Structure :
map[string]*entry+container/liststdlib pour l'ordre d'éviction - Pas de dépendance externe
Gestion d'erreurs
| Situation | Comportement |
|---|---|
| Fichier introuvable | Erreur avec chemin complet |
| Fichier non gzippé | Tentative de lecture en texte brut |
| Format cachegrind invalide | Erreur avec numéro de ligne |
| Fonction non trouvée (exact) | Message clair + liste des matches contains si disponible |
| Fichier très gros / OOM | Non géré à P0 — limite documentée |
Wiring
mcp.toml
binary_name = "xdebug-mcp"
[bootstrap]
description = "MCP server for Xdebug profiling files"
[profiles]
default = "prod"
known = ["dev", "prod"]
cmd/xdebug-mcp/main.go utilise bootstrap.Run avec uniquement le hook MCP branché sur app.RunMCP. La Description est soit fournie par mcpgen (à vérifier après génération), soit hardcodée.
internal/app/app.go :
- Instancie
cache.New(cache.DefaultCapacity) - Crée le serveur
mcp-go - Enregistre les trois tools
- Lance
server.ServeStdio()
mcpgen/ est généré via mcp-framework generate et doit être regénéré après toute modification de mcp.toml. Il expose au minimum mcpgen.BinaryName.
Limites P0 connues
- Pas de guard mémoire : un fichier de 325 Mo décompressé est chargé intégralement en RAM
- Pas de list_profiles : l'appelant doit connaître le chemin du fichier
- Pas d'auto-update configuré dans
mcp.toml - Pas de secrets ni de config persistante (pas de
[[config.fields]])