IMPORTANT : Comment utiliser cette compétence
Ce fichier fournit un GUIDE DE NAVIGATION UNIQUEMENT. Avant d'implémenter une quelconque fonctionnalité de serveur MCP, tu DOIS :
- Lire cet aperçu pour comprendre quels fichiers de référence sont pertinents
- TOUJOURS lire le ou les fichiers de référence spécifiques pour les fonctionnalités que tu implémentes
- Appliquer les motifs détaillés de ces fichiers à ton implémentation
Ne te fie PAS uniquement aux exemples rapides de ce fichier — ils sont minimalistes. Les fichiers de référence contiennent les meilleures pratiques critiques, les considérations de sécurité et les motifs avancés.
Meilleures pratiques de serveur MCP
Guide complet pour construire des serveurs MCP prêts pour la production avec outils, ressources, prompts et widgets utilisant mcp-use.
⚠️ AVANT TOUT : Nouveau projet ou projet existant ?
Avant de faire quoi que ce soit, détermine si tu es à l'intérieur d'un projet mcp-use existant.
Détection : Vérifie l'espace de travail pour un package.json qui liste "mcp-use" comme dépendance, OU tout fichier .ts qui importe depuis "mcp-use/server".
├─ Projet mcp-use TROUVÉ → Ne fais PAS de scaffolding. Tu es déjà dans un projet.
│ └─ Saute à « Quick Navigation » ci-dessous pour ajouter des fonctionnalités.
│
├─ Pas de projet mcp-use (répertoire vide, projet non lié ou greenfield)
│ └─ Fais d'abord le scaffolding avec npx create-mcp-use-app, puis ajoute les fonctionnalités.
│ Voir « Scaffolding a New Project » ci-dessous.
│
└─ À l'intérieur d'un projet NON LIÉ (p. ex. app Next.js) et l'utilisateur veut un serveur MCP
└─ Demande à l'utilisateur où le créer, puis fais le scaffolding dans ce répertoire.
Ne fais PAS le scaffolding à l'intérieur d'une racine de projet non lié existante.
NE crée JAMAIS manuellement le boilerplate MCPServer, package.json ou la structure du projet. La CLI configure TypeScript, les scripts de développement, l'intégration de l'inspecteur, le rechargement à chaud et la compilation des widgets, ce qui est difficile à reproduire manuellement.
Scaffolding d'un nouveau projet
npx create-mcp-use-app my-server
cd my-server
npm run dev
Pour les détails complets du scaffolding et les drapeaux CLI, voir quickstart.md.
Navigation rapide
Choisis ton chemin en fonction de ce que tu construis :
🚀 Fondations
Quand : TOUJOURS lire d'abord quand tu commences un travail MCP dans une nouvelle conversation. Référence ultérieurement pour clarifier l'architecture/les concepts.
- concepts.md — Primitives MCP (Tool, Resource, Prompt, Widget) et quand utiliser chacune
- architecture.md — Structure du serveur (basée sur Hono), système de middleware, server.use() vs server.app
- quickstart.md — Scaffolding, configuration et premier exemple d'outil
- deployment.md — Déployer sur Manufact Cloud, auto-hébergement, Docker, gérer les déploiements
Charge-les avant de plonger dans les sections outils/ressources/widgets.
🔐 Ajouter l'authentification ?
Quand : Protéger ton serveur avec OAuth (Auth0, Better Auth, WorkOS, Supabase, Keycloak, ou tout autre fournisseur)
-
- Quand : Première fois ajoutant l'authentification, comprenant
ctx.auth, ou choisissant un fournisseur / mode d'intégration - Couvre : Authentification distante vs proxy OAuth, config
oauth, forme dectx.auth, comparaison des fournisseurs, erreurs courantes
- Quand : Première fois ajoutant l'authentification, comprenant
-
- Quand : Utilisant Auth0 — DCR (Early Access) ou une Regular Web App standard via
oauthProxy - Couvre : Configuration pour les deux modes,
extraAuthorizeParams.audience, permissions viarfc9068_profile_authz
- Quand : Utilisant Auth0 — DCR (Early Access) ou une Regular Web App standard via
-
- Quand : Utilisant Better Auth avec le plugin
@better-auth/oauth-provider(OAuth 2.1 auto-hébergé) - Couvre :
oauthBetterAuthProvider, routes d'URL auth / métadonnées, flux de connexion et consentement
- Quand : Utilisant Better Auth avec le plugin
-
- Quand : Utilisant WorkOS AuthKit (DCR uniquement)
- Couvre : Configuration, variables env, rôles/permissions, filtrage org multi-tenant, appels API WorkOS
-
- Quand : Utilisant le serveur OAuth 2.1 de Supabase
- Couvre : Configuration, clés publiables, ES256 vs HS256, héberger l'interface de consentement, appels SDK RLS-aware
-
- Quand : Utilisant Keycloak via DCR natif
- Couvre : Hôtes de confiance DCR + origines web, application de l'audience, rôles realm vs ressource, userinfo
-
- Quand : Tout autre fournisseur — capable de DCR via
oauthCustomProvider, ou pré-enregistré (Google, GitHub, Okta, Azure AD) viaoauthProxy - Couvre :
oauthCustomProvider,oauthProxy+jwksVerifier, exemples de fournisseur, vérification de jeton opaque
- Quand : Tout autre fournisseur — capable de DCR via
🔧 Construire un backend de serveur (pas d'interface utilisateur) ?
Quand : Implémenter des fonctionnalités MCP (actions, données, templates). Lis le fichier spécifique pour la primitive que tu construis.
-
- Quand : Créer des actions backend que l'IA peut appeler (send-email, fetch-data, create-user)
- Couvre : Définition d'outil, schémas, annotations, contexte, gestion d'erreurs
-
- Quand : Exposer des données en lecture seule que les clients peuvent récupérer (config, profils utilisateur, documentation)
- Couvre : Ressources statiques, ressources dynamiques, templates de ressources paramétrés, complément URI
-
- Quand : Créer des templates de messages réutilisables pour les interactions IA (code-review, summarize)
- Couvre : Définition de prompt, paramétrisation, complément d'arguments, meilleures pratiques de prompt
-
- Quand : Formatter les réponses des outils/ressources (texte, JSON, markdown, images, erreurs)
- Couvre :
text(),object(),markdown(),image(),error(),mix()
-
- Quand : Composer plusieurs serveurs MCP en un serveur agrégateur unifié
- Couvre :
server.proxy(), API config, sessions explicites, routage d'échantillonnage
-
- Quand : Ajouter de la logique transversale (logging, vérifications auth, rate limiting, filtrage d'outils) qui s'étend sur plusieurs outils/ressources
- Couvre : Middleware
server.use('mcp:...'),MiddlewareContext(method, params, auth, state), correspondance de motifs, middleware HTTP vs MCP
🎨 Construire des widgets visuels (interface utilisateur interactive) ?
Quand : Créer des interfaces React pour parcourir, comparer ou sélectionner des données
-
- Quand : Créer ton premier widget ou ajouter une interface à un outil existant
- Couvre : Configuration du widget, hook
useWidget(), vérificationsisPending, manipulation des props
-
- Quand : Gérer l'état de l'interface (sélections, filtres, onglets) dans les widgets
- Couvre :
useState,setState, persistance d'état, quand utiliser l'état d'outil vs widget
-
- Quand : Ajouter des boutons, formulaires ou appeler des outils depuis les widgets
- Couvre :
useCallTool(), manipulation de formulaires, boutons d'action, mises à jour optimistes
-
- Quand : Styliser les widgets pour supporter les thèmes, dispositions réactives ou accessibilité
- Couvre :
useWidgetTheme(), mode clair/sombre,autoSize, motifs de disposition, meilleures pratiques CSS
-
- Quand : Construire des widgets complexes avec données async, limites d'erreurs ou optimisations de performance
- Couvre : États de chargement, gestion d'erreurs, mémoïsation, fractionnement de code
-
- Quand : Garder le modèle IA conscient de ce que l'utilisateur voit actuellement (onglet actif, élément survolé, produit sélectionné) sans nécessiter d'appels d'outils
- Couvre : Composant
<ModelContext>, API impérativemodelContext.set/remove, imbrication, sérialisation d'arbre, règles de cycle de vie
-
- Quand : Télécharger ou téléverser des fichiers depuis un widget (SDK ChatGPT Apps uniquement)
- Couvre : Hook
useFiles(), gardeisSupported, visibilité du modèle (modelVisible), stockage defileId, URL de téléchargement temporaires
📚 Besoin d'exemples complets ?
Quand : Tu veux voir des implémentations complètes de cas d'usage courants
- common-patterns.md
- Exemples de bout en bout : application météo, liste de tâches, navigateur de recettes
- Affiche : Code serveur + code widget + meilleures pratiques en contexte
Arbre de décision
De quoi as-tu besoin ?
├─ Nouveau projet à partir de zéro
│ └─> quickstart.md (scaffolding + configuration)
│
├─ OAuth / authentification utilisateur
│ └─> authentication/overview.md → guide spécifique au fournisseur
│
├─ Action backend simple (pas d'interface)
│ └─> Utiliser Tool : server/tools.md
│
├─ Données en lecture seule pour les clients
│ └─> Utiliser Resource : server/resources.md
│
├─ Template de prompt réutilisable
│ └─> Utiliser Prompt : server/prompts.md
│
├─ Logique transversale (logging, vérifications auth, rate limiting, filtrage d'outils)
│ └─> Utiliser Middleware : architecture.md#mcp-middleware
│
├─ Interface utilisateur visuelle/interactive
│ └─> Utiliser Widget : widgets/basics.md
│
├─ Garder le modèle conscient de ce que l'utilisateur voit dans le widget
│ └─> widgets/model-context.md
├─ Télécharger/téléverser des fichiers dans un widget
│ └─> widgets/files.md (SDK ChatGPT Apps uniquement)
│
└─ Déployer en production
└─> deployment.md (déploiement cloud, auto-hébergement, Docker)
Principes fondamentaux
- Outils pour les actions — Opérations backend avec entrée/sortie
- Ressources pour les données — Données en lecture seule que les clients peuvent récupérer
- Prompts pour les templates — Templates de messages réutilisables
- Widgets pour l'interface — Interfaces visuelles quand utile
- Données fictives d'abord — Prototype rapidement, connecte les API plus tard
❌ Erreurs courantes
Évite ces anti-motifs trouvés dans les serveurs MCP en production :
Définition d'outil
- ❌ Retourner des objets bruts au lieu d'utiliser les helpers de réponse
- ✅ Utilise les helpers
text(),object(),widget(),error()
- ✅ Utilise les helpers
- ❌ Sauter
.describe()du schéma Zod sur chaque champ- ✅ Ajoute des descriptions à tous les champs du schéma pour une meilleure compréhension de l'IA
- ❌ Pas de validation ou sanitisation d'entrée
- ✅ Valide les entrées avec Zod, sanitise les données fournies par l'utilisateur
- ❌ Lancer des erreurs au lieu d'utiliser le helper
error()- ✅ Utilise
error("message")pour des réponses d'erreur gracieuses
- ✅ Utilise
Développement de widget
- ❌ Accéder à
propssans vérifierisPending- ✅ Toujours vérifier
if (isPending) return <Loading/>
- ✅ Toujours vérifier
- ❌ Le widget gère l'état du serveur (filtres, sélections)
- ✅ Les widgets gèrent leur propre état d'interface avec
useState
- ✅ Les widgets gèrent leur propre état d'interface avec
- ❌ Wrapper
McpUseProvidermanquant ouautoSize- ✅ Enveloppe le composant root :
<McpUseProvider autoSize>
- ✅ Enveloppe le composant root :
- ❌ Styles en ligne sans sensibilité au thème
- ✅ Utilise
useWidgetTheme()pour le support du mode clair/sombre
- ✅ Utilise
Sécurité et production
- ❌ Clés API ou secrets codés en dur dans le code
- ✅ Utilise
process.env.API_KEY, documente dans.env.example
- ✅ Utilise
- ❌ Pas de gestion d'erreurs dans les handlers d'outils
- ✅ Enveloppe dans try/catch, retourne
error()en cas d'échec
- ✅ Enveloppe dans try/catch, retourne
- ❌ Opérations coûteuses sans caching
- ✅ Cache les appels API, les calculs avec TTL
- ❌ Configuration CORS manquante
- ✅ Configure CORS pour les déploiements en production
🔒 Règles d'or
Directives architecturales opinionnées :
1. Un outil = Une capacité
Divise les actions larges en outils ciblés :
- ❌
manage-users(trop vague) - ✅
create-user,delete-user,list-users
2. Retourner les données complètes d'emblée
Les appels d'outils sont coûteux. Évite le chargement paresseux :
- ❌
list-products+get-product-details(2 appels) - ✅
list-productsretourne les données complètes incluant les détails
3. Les widgets possèdent leur état
L'état de l'interface vit dans le widget, pas dans des outils séparés :
- ❌ Outil
select-item, outilset-filter - ✅ Le widget gère avec
useStateousetState
4. exposeAsTool par défaut à false
Les widgets sont enregistrés comme ressources uniquement par défaut. Utilise un outil personnalisé (recommandé) ou définis exposeAsTool: true pour exposer un widget au modèle :
// ✅ LES 4 ÉTAPES REQUISES pour l'inférence de type appropriée :
// Étape 1 : Définir le schéma séparément
const propsSchema = z.object({
title: z.string(),
items: z.array(z.string())
});
// Étape 2 : Référencer la variable du schéma dans les métadonnées
export const widgetMetadata: WidgetMetadata = {
description: "...",
props: propsSchema, // ← PAS z.object() en ligne
exposeAsTool: false
};
// Étape 3 : Inférer le type Props à partir de la variable du schéma
type Props = z.infer<typeof propsSchema>;
// Étape 4 : Utiliser les Props typés avec useWidget
export default function MyWidget() {
const { props, isPending } = useWidget<Props>(); // ← Ajouter <Props>
// ...
}
⚠️ Erreur courante : Faire seulement les étapes 1-2 mais sauter 3-4 (perd la sécurité de type)
5. Valider aux limites uniquement
- Fais confiance au code interne et aux garanties du framework
- Valide l'entrée utilisateur, les réponses API externes
- N'ajoute pas de gestion d'erreurs pour les scénarios qui ne peuvent pas se produire
6. Préférer les widgets pour parcourir/comparer
En cas de doute, ajoute un widget. L'interface visuelle améliore :
- Le parcours de plusieurs éléments
- La comparaison de données côte à côte
- Les workflows de sélection interactive
Référence rapide
Serveur minimal
import { MCPServer, text } from "mcp-use/server";
import { z } from "zod";
const server = new MCPServer({
name: "my-server",
title: "My Server",
version: "1.0.0"
});
server.tool(
{
name: "greet",
description: "Greet a user",
schema: z.object({ name: z.string().describe("User's name") })
},
async ({ name }) => text("Hello " + name + "!"),
);
server.listen();
Helpers de réponse
| Helper | Utiliser quand | Exemple |
|---|---|---|
text() |
Réponse simple en string | text("Success!") |
object() |
Données structurées | object({ status: "ok" }) |
markdown() |
Texte formaté | markdown("# Title\nContent") |
widget() |
Interface visuelle | widget({ props: {...}, output: text(...) }) |
mix() |
Contenus multiples | mix(text("Hi"), image(url)) |
error() |
Réponses d'erreur | error("Failed to fetch data") |
resource() |
Intégrer des renvois de ressources | resource("docs://guide", "text/markdown") |
Méthodes du serveur :
server.tool()— Définir un outil exécutableserver.resource()— Définir une ressource statique/dynamiqueserver.resourceTemplate()— Définir une ressource paramétréeserver.prompt()— Définir un template de promptserver.proxy()— Composer/Proxy plusieurs serveurs MCPserver.uiResource()— Définir une ressource widgetserver.listen()— Démarrer le serveurserver.use('mcp:tools/call', fn)— Middleware MCP (outils, ressources, prompts, opérations list)server.use('mcp:*', fn)— Middleware MCP fourre-toutserver.use(fn)— Middleware HTTP (Hono)