Guide participant

TP1 — Générer un serveur MCP vanilla

Piloter OpenCode pour obtenir un serveur fonctionnel, puis observer ce que le modèle ne normalise pas tout seul.

Objectif et livrables

Faites produire le Java par l’agent : votre travail est de cadrer la demande, relire les changements et vérifier le résultat. À la fin, conservez un serveur qui démarre, les preuves de ses appels MCP et un tableau des écarts.

Prérequis : la préparation est terminée. Support : consigne du TP1.

Essentiel et approfondissement

Parcours Sections concernées Preuve de sortie
Essentiel — 1 h 30 cible 1 à 7 : une génération vanilla, build, démarrage, inspecteur, comparaison avec un voisin, baseline, test depuis OpenCode Deux tools find_service / get_owner, resource service://{name}, prompt fiche_service observés ; appels vérifiés ; écarts éventuels et conditions conservés ; au moins un tool appelé par OpenCode
Approfondissement — hors 14 h ou si avance 5 : seconde génération individuelle indépendante et nouvelle comparaison Deux sorties séparées et variabilité documentée, sans supposer un échec

La durée suppose la préparation vérifiée ; les corrections nécessaires au fonctionnement restent essentielles. L’approfondissement ne remplace aucune preuve.

1. Créer un projet isolé

Dans votre terminal préparé :

mkdir "$ATELIER_DIR/serveur-vanilla"
cd "$ATELIER_DIR/serveur-vanilla"
git init
cp "$FORMATION_REPO/domaine/catalogue-services.json" .
opencode

Si le dossier existe déjà, choisissez un nouveau nom et utilisez-le dans les commandes suivantes. Ne mélangez pas deux générations. Ne copiez ni Skill ni conventions dans ce projet.

2. Donner la consigne à l’agent

Utilisez ce point de départ et conservez le texte exact :

Crée dans le répertoire courant un serveur MCP Java avec Quarkus.
Le domaine est le catalogue local catalogue-services.json.
Il faut exposer :
- un tool find_service(query), pour chercher un service par nom ou équipe ;
- un tool get_owner(service), pour obtenir son équipe et sa criticité ;
- une resource service://{name}, pour lire sa fiche complète ;
- un prompt fiche_service, pour préparer une synthèse d'astreinte.
Ne fais aucun appel à une API métier externe.
Génère le projet, les tests et les commandes pour le lancer.
Compile et exécute les tests, puis explique comment inspecter les primitives MCP.
N'utilise aucun Skill pour cet exercice.

Laissez l’agent proposer une structure et un transport. Relisez les commandes avant de les autoriser. S’il pose une question, répondez et notez votre décision : cela fait partie des conditions de génération.

Vous devez obtenir au minimum un pom.xml, du code sous src/main/java, les données nécessaires à l’exécution et des tests. Lisez le pom.xml pour relever les versions de Java, de Quarkus et de l’extension MCP utilisées.

3. Construire et lancer réellement

Depuis serveur-vanilla, dans un autre terminal si OpenCode est encore ouvert :

mvn test
mvn quarkus:dev

Le premier appel doit terminer sans erreur ; vérifiez qu’il a effectivement exécuté des tests. Le second reste actif : attendez le message de démarrage Quarkus. Si le projet fournit un wrapper Maven, ./mvnw peut remplacer mvn.

Une erreur de résolution de dépendance n’est pas une erreur de logique métier. Une annotation ou une signature MCP invalide peut empêcher le build ou le démarrage. Transmettez à l’agent la commande et le message d’erreur exacts, demandez une correction ciblée, puis rejouez les mêmes commandes.

Voici la commande exécutée et son erreur : [coller la sortie utile].
Identifie la cause, corrige le projet sans retirer les primitives demandées,
puis relance la validation. Explique les fichiers modifiés.

Ne contournez pas un échec en supprimant un test ou une primitive.

4. Inspecter le contrat MCP

Pour un serveur HTTP, ouvrez la Dev UI à l’adresse indiquée dans les logs (souvent http://localhost:8080/q/dev-ui/) et cherchez l’outil d’inspection MCP fourni par l’extension. Selon sa version, les intitulés et l’accès à l’inspecteur peuvent varier : suivez le lien exposé par votre serveur.

Si le modèle a choisi stdio ou si aucun inspecteur n’est intégré, utilisez un client MCP compatible avec ce transport. Consultez la documentation de l’extension et, si nécessaire, le MCP Inspector. Ne déduisez pas une URL MCP du seul port HTTP : relevez le point d’accès documenté par le projet.

Listez séparément les tools, les templates de resources et les prompts. Une resource paramétrée apparaît dans les templates, pas forcément dans la liste des resources statiques. Vérifiez les arguments réellement exposés, puis exécutez :

Appel Résultat métier à retrouver
find_service avec query = "auth" Le service auth
find_service avec query = "Team Catalog" catalogue-produits et recherche
get_owner avec service = "facturation" Team Billing, criticité haute
Lecture de service://facturation Dépendances auth et catalogue-produits, équipe et criticité
Récupération du prompt fiche_service Un gabarit de synthèse exploitable pour l’astreinte

Pour le prompt, renseignez les arguments indiqués par son schéma : la consigne n’en fixe pas la signature. Récupérer un prompt MCP ne lance pas automatiquement une génération LLM.

Essayez aussi un service inconnu. Notez le comportement sans inventer une convention qui n’existe pas encore : liste vide, absence ou erreur explicite doivent être discutées. Aucun résultat ne doit inventer un propriétaire.

5. Faire le constat de conformité

Seulement après la génération, ouvrez les conventions maison et la référence formateur. Dans le parcours essentiel, comparez votre projet à celui d’un voisin. La seconde génération individuelle indépendante est un approfondissement : conservez-la séparément si vous la réalisez.

Complétez un tableau dans vos notes :

Critère Observation dans mon projet Écart à traiter en TP2
Extension et transport Dépendance exacte du pom.xml HTTP attendu, stdio seulement en option
Package Package racine généré Cible : com.sciam.formation.mcp
Métier et adaptateur Qui charge les données ? Qui porte les annotations ? Deux responsabilités séparées
Noms publics Noms vus dans l’inspecteur, pas seulement les méthodes Java find_service, get_owner, fiche_service
Resource URI et argument exposés service://{name}
Descriptions Annotations des tools et arguments Descriptions explicites
Données et démarrage Chargement JSON, tests, logs Classpath, test de démarrage, logs INFO

Ne déclarez pas un écart sans preuve dans un fichier ou dans l’inspecteur. Si votre modèle respecte déjà tout, notez-le : le TP ne cherche pas à provoquer un échec. La conformité d’une génération ne prouve pas encore sa répétabilité.

6. Figer la baseline

Laissez le dev mode tourner : la section 7 en a besoin. Gardez ce projet intact pour la comparaison. Vous pouvez le versionner localement après avoir vérifié .gitignore et git status : ni target/, ni secrets, ni configuration personnelle ne doivent être ajoutés. Ne publiez rien avant cette vérification.

Votre compte rendu doit contenir le prompt, les versions, les corrections demandées, les résultats des appels et le tableau des écarts.

7. Tester le serveur avec OpenCode

L’inspecteur a prouvé le contrat ; il reste à prouver qu’un agent s’en sert. OpenCode est aussi un client MCP : branchez-lui votre serveur et faites-lui répondre à des questions métier qu’il ne peut résoudre qu’en appelant vos tools. C’est la chaîne d’utilisation du fil rouge, celle que les équipes produit emprunteront.

Le serveur doit tourner (mvn quarkus:dev). Créez un dossier client distinct du projet généré, pour ne pas modifier la baseline :

mkdir "$ATELIER_DIR/client-mcp"
cd "$ATELIER_DIR/client-mcp"

Créez-y un fichier opencode.json, en reportant le point d’accès documenté par votre projet. Avec quarkus-mcp-server-http, le serveur expose par défaut le transport Streamable HTTP sur http://localhost:8080/mcp et le transport SSE historique sur http://localhost:8080/mcp/sse ; adaptez le port si vous l’avez changé.

{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "serveur-vanilla": {
      "type": "remote",
      "url": "http://localhost:8080/mcp",
      "enabled": true
    }
  }
}

Si le modèle a choisi stdio, déclarez un serveur "type": "local" avec la "command" documentée par le projet, par exemple java -jar sur l’artefact produit par mvn package. Le dev mode écrit sur la console : ne le mélangez pas avec un transport stdio. Le format exact est décrit dans la documentation OpenCode ; vérifiez-le pour la version installée.

Vérifiez la connexion avant d’ouvrir une session :

opencode mcp list

Le serveur doit apparaître connecté. Sinon, comparez l’URL avec les logs Quarkus et la documentation de l’extension, et consultez opencode mcp debug serveur-vanilla. Lancez ensuite opencode depuis client-mcp, avec le même modèle qu’en section 2, et posez des questions qui imposent l’appel d’un tool :

Quelle équipe est propriétaire du service facturation, et quelle est sa criticité ?
Utilise uniquement les outils MCP disponibles. Si tu ne trouves pas, dis-le sans deviner.
Liste les services gérés par Team Catalog, avec les outils MCP disponibles.
Qui est propriétaire du service paiement-express ? N'invente aucune réponse.

OpenCode enregistre les tools sous la forme serveur_tool, ici serveur-vanilla_get_owner et serveur-vanilla_find_service. Dans la session, relevez pour chaque question :

À relever Preuve attendue
Tool appelé et arguments get_owner avec service = "facturation" ; find_service avec query = "Team Catalog"
Résultat brut renvoyé par le serveur Les mêmes valeurs qu’en section 4 : Team Billing, criticité haute ; catalogue-produits et recherche
Réponse finale de l’agent Fidèle au résultat du tool, sans propriétaire inventé pour paiement-express

Une réponse correcte sans appel de tool ne prouve rien : le modèle a pu lire le JSON ou improviser. Une réponse qui contredit le résultat du tool est un écart à noter. Si l’agent n’appelle pas le serveur, reformulez en nommant le tool ; si le tool est appelé mais échoue, transmettez l’erreur à la session de génération comme en section 3.

La resource et le prompt ne sont pas forcément exposés par le client : la prise en charge des resources et des prompts MCP dépend de la version d’OpenCode. Ne concluez pas à une absence côté serveur : l’inspecteur de la section 4 reste la preuve pour ces deux primitives. Notez ce que le client montre et ce qu’il ignore.

Ajoutez à votre compte rendu le fichier opencode.json, les appels de tools observés avec leurs arguments et le comportement sur le service inconnu. Arrêtez ensuite le dev mode avec Ctrl+C.

Dépannage

Symptôme Action
Port 8080 occupé Arrêter votre autre serveur ou utiliser mvn quarkus:dev -Dquarkus.http.port=8081
JSON introuvable au lancement Vérifier où le code cherche le fichier et si la ressource est empaquetée
Build vert, primitives absentes Vérifier l’extension, les annotations et les listes de l’inspecteur
Méthode Java présente mais nom MCP incorrect Comparer le nom public enregistré, pas seulement le nom de méthode
Modèle bloqué sur une dépendance Lui fournir l’erreur Maven et la documentation de la version choisie
opencode mcp list ne voit pas le serveur Vérifier que le dev mode tourne, l’URL et le port, le transport déclaré (remote pour HTTP, local pour stdio)
OpenCode répond sans appeler de tool Reformuler en nommant le tool ; vérifier qu’il apparaît dans la liste des tools de la session

Point de passage

Passez au TP2 lorsque les quatre primitives ont été observées, que les appels métier sont vérifiés, qu’OpenCode a appelé au moins un tool de votre serveur, que la baseline et la comparaison avec un voisin sont conservées et que vous savez distinguer fonctionnement et conformité. Un tableau sans écart est recevable s’il est étayé ; une seconde génération individuelle n’est pas requise pour ce passage.