La façade tient les serveurs MCP quand LOKI_MCP=rust (expérimental ; le Go reste le défaut) : configuration (state/mcp), catalogue embarqué (le même fichier que le Go, copié dans l'étape Rust de l'image), état et routes /api/mcp*. Un client MCP en Rust (mcp/client.rs) parle JSON-RPC sur stdio et en Streamable HTTP (réponses JSON ou SSE, session suivie, en-têtes configurés, ping du serveur servi). Le pool suit mcp_client.go : session gardée entre les tours, connexions parallèles et partagées, session invalidée pendant sa connexion jamais republiée, délais npx/uvx, appel borné avec une reconnexion, sortie plafonnée à la fenêtre. Un seul pool, celui de la façade : le Go, qui exécute encore les outils de l'agent, lui demande les outils MCP, leurs appels et le pré-chauffage (/internal/facade/v1/mcp/*, facade_mcp.go). Parité : parity_mcp_test.go tire du Go les fonctions pures, seize mutations de configuration, et un vrai serveur du SDK MCP Go (onze outils) : ce qu'il met sur le fil et ce que le client Go en tire (état, outils annoncés, appels). tests/mcp.rs le rejoue contre un faux serveur qui resert ces réponses, et vérifie stdio, reconnexion, routes, catalogue et session invalidée de bout en bout ; TestFacadeMCP côté Go. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016tcGfJWefW5kUGoC2wL8cv
4.2 KiB
Lot 20 — serveurs MCP tenus par la façade
Contexte
Le Go tenait les serveurs MCP (Model Context Protocol) :
mcp_config.go: la configuration, dansstate/mcp;mcp_catalog.go: le catalogue embarqué ;mcp_client.go: le pool de sessions, avec le SDK MCP Go ;- les routes
/api/mcp*deweb_api.go.
Le pool sert deux usages : l'état affiché dans l'interface, et les outils
mcp__<serveur>__<outil> annoncés au modèle et exécutés pour l'agent.
Lot 20 : la façade tient tout cela quand LOKI_MCP=rust (mcp/,
expérimental). Sans la variable, rien ne change.
| Route | Rôle |
|---|---|
GET /api/mcp |
état de chaque serveur, connexion tentée pour les activés (en parallèle) |
GET /api/mcp/catalog |
catalogue embarqué (le même fichier que le Go), {workspace} résolu, entrées déjà configurées marquées, lanceur (npx, uvx) présent ou non |
POST /api/mcp/save |
ajoute ou remplace (actif par défaut) ; nom contrôlé, un seul transport |
POST /api/mcp/delete, /api/mcp/toggle, /api/mcp/tool |
retire, active ou éteint, masque ou démasque un outil |
POST /api/mcp/test |
reconnexion forcée |
Client MCP en Rust
mcp/client.rs parle JSON-RPC 2.0 sur deux transports, comme le SDK Go :
- stdio : un processus local, un message par ligne ;
- Streamable HTTP : un POST par message, réponse en JSON ou en flux SSE,
session suivie par
Mcp-Session-Id, en-têtes configurés ajoutés.
La session s'ouvre par initialize puis notifications/initialized. Elle
sert tools/list (première page, comme le Go) et tools/call. Un ping du
serveur reçoit sa réponse.
Le pool (mcp/mod.rs) suit mcp_client.go :
- une session gardée d'un tour à l'autre ;
- une entrée publiée avant la connexion, pour que deux appelants partagent la même tentative ;
- une session invalidée pendant sa connexion n'est jamais republiée ;
- délai de 3 min pour npx et uvx (premier téléchargement), 20 s sinon ;
- un appel borné à 120 s, une reconnexion si la session est morte ;
- la sortie plafonnée à la fenêtre de contexte (12 000 au moins), une erreur d'outil signalée, une sortie vide rendue « [ok] (aucune sortie) ».
Le Go
Un seul pool, celui de la façade. Le Go, qui exécute encore les outils de
l'agent, lui demande les outils à annoncer, leurs appels et le
pré-chauffage (/internal/facade/v1/mcp/{tools,call,prewarm},
facade_mcp.go), quelle que soit la conversation qui tourne. Son propre
pool reste vide.
L'image copie internal/loki/mcp_catalog.json dans l'étape de compilation
Rust : un seul catalogue pour les deux binaires.
Écarts assumés
- Les erreurs de transport (connexion refusée, flux coupé) ont les messages du client Rust. Une erreur JSON-RPC et un binaire introuvable ont ceux du Go.
- Un contenu « autre » (lien de ressource, ressource intégrée) est rendu en JSON dans l'ordre des champs du SDK. Les annotations éventuelles sont dans l'ordre alphabétique.
Parité
internal/loki/parity_mcp_test.go tire du Go :
- les fonctions pures : noms exposés, schémas, libellés d'appel, serveur d'origine d'un outil, ligne du préambule, délais et leurs messages, aplatissement de neuf résultats ;
- la lecture de la configuration et seize mutations avec leurs erreurs et la base après chacune ;
- ce qu'un vrai serveur du SDK MCP Go met sur le fil en Streamable HTTP : onze outils, et le résultat de chacun ;
- ce que le client Go en tire : état de quatre serveurs (joignable, binaire introuvable, désactivé, mal configuré), outils annoncés (un masqué), appels.
tests/mcp.rs rejoue les fonctions et la configuration. Un faux serveur
resert ce que le serveur du SDK a répondu (appels en SSE) : l'état, les
outils et les appels tirés par la façade doivent être ceux du Go, sur une
seule session. Sont aussi vérifiés de bout en bout :
- le transport stdio : environnement, ping du serveur, serveur mort en plein appel puis reconnexion ;
- les routes de configuration, en-têtes compris ;
- le catalogue ;
- une session invalidée pendant sa connexion, sans processus laissé derrière.
Une mutation de chaque règle fait échouer les tests. Côté Go,
TestFacadeMCP vérifie le relais, les outils, les appels, le
pré-chauffage et une façade injoignable.