Construire un serveur MCP de A à Z : tools, resources, prompts, validation, transports stdio et HTTP — pour connecter Claude à n'importe quel système externe.
Les prompts MCP sont des templates de messages que le serveur expose et que les clients peuvent instancier. Moins courants que les tools et resources, ils deviennent utiles quand vous avez des workflows de conversation répétitifs — revue de code avec un contexte précis, analyse de logs selon un format défini, génération de rapport avec des paramètres.
Un prompt MCP est utile quand :
Exemple : un prompt analyze_error qui récupère les logs autour d'une erreur, les formate, et demande à Claude une analyse précise.
const server = new Server(
{ name: 'mon-mcp', version: '1.0.0' },
{
capabilities: {
tools: {},
resources: {},
prompts: {}, // ← activer les prompts
},
}
)import {
ListPromptsRequestSchema,
GetPromptRequestSchema,
} from '@modelcontextprotocol/sdk/types.js'
server.setRequestHandler(ListPromptsRequestSchema, async () => ({
prompts: [
{
name: 'analyze_error',
description: 'Analyse une erreur avec le contexte des logs environnants',
arguments: [
{
name: 'error_id',
description: 'ID de l\'erreur dans le système de logs',
required: true,
},
{
name: 'context_lines',
description: 'Nombre de lignes de contexte avant/après (défaut: 20)',
required: false,
},
],
},
{
name: 'code_review',
description: 'Revue de code avec focus configurable',
arguments: [
{
name: 'file_path',
description: 'Chemin du fichier à reviewer',
required: true,
},
{
name: 'focus',
description: 'Focus de la review : security | performance | readability | all',
required: false,
},
],
},
{
name: 'weekly_report',
description: 'Génère un rapport hebdomadaire depuis les données du projet',
arguments: [
{
name: 'week',
description: 'Semaine au format YYYY-WNN (ex: 2026-W25)',
required: false,
},
],
},
],
}))Le handler retourne une liste de messages prêts à être envoyés à Claude.
server.setRequestHandler(GetPromptRequestSchema, async (request) => {
const { name, arguments: args } = request.params
if (name === 'analyze_error') {
const errorId = args?.error_id as string
const contextLines = parseInt(args?.context_lines as string ?? '20')
// Récupérer les données depuis votre système
const error = await logsService.getError(errorId)
const context = await logsService.getContext(errorId, contextLines)
if (!error) {
throw new McpError(ErrorCode.InvalidParams, `Erreur ${errorId} introuvable`)
}
return {
description: `Analyse de l'erreur ${errorId}`,
messages: [
{
role: 'user',
content: {
type: 'text',
text: `Analyse cette erreur :
**Erreur :** ${error.message}
**Timestamp :** ${error.timestamp}
**Service :** ${error.service}
**Stack trace :**
\`\`\`
${error.stackTrace}
\`\`\`
**Contexte (${contextLines} lignes avant/après) :**
\`\`\`
${context}
\`\`\`
Identifie la cause racine, explique pourquoi ça s'est produit, et propose des solutions.`,
},
},
],
}
}
if (name === 'code_review') {
const filePath = args?.file_path as string
const focus = (args?.focus as string) ?? 'all'
const fileContent = await readFile(filePath, 'utf-8')
const extension = filePath.split('.').pop()
const focusInstructions: Record<string, string> = {
security: 'Focus sur les vulnérabilités de sécurité : injection, exposition de données, authentification manquante, validation insuffisante.',
performance: 'Focus sur les problèmes de performance : requêtes N+1, algorithmes inefficaces, fuites mémoire, calculs inutiles.',
readability: 'Focus sur la lisibilité : nommage, complexité cyclomatique, duplication, abstractions manquées.',
all: 'Analyse complète : sécurité, performance, lisibilité, et maintenabilité.',
}
return {
description: `Review de ${filePath} (focus: ${focus})`,
messages: [
{
role: 'user',
content: {
type: 'text',
text: `Review ce fichier :
**Fichier :** ${filePath}
**Focus :** ${focusInstructions[focus] ?? focusInstructions.all}
\`\`\`${extension}
${fileContent}
\`\`\`
Présente tes findings par sévérité : Critique / Warning / Suggestion. Pour chaque finding : ligne concernée, problème, correction proposée.`,
},
},
],
}
}
if (name === 'weekly_report') {
const week = args?.week as string ?? getCurrentWeek()
// Récupérer les données du projet pour la semaine
const commits = await gitService.getCommitsForWeek(week)
const closedTickets = await ticketsService.getClosedForWeek(week)
const deployments = await deployService.getForWeek(week)
return {
description: `Rapport hebdomadaire ${week}`,
messages: [
{
role: 'user',
content: {
type: 'text',
text: `Génère un rapport hebdomadaire structuré à partir de ces données.
**Semaine :** ${week}
**Commits (${commits.length}) :**
${commits.map(c => `- ${c.hash.slice(0,7)} ${c.message} (${c.author})`).join('\n')}
**Tickets fermés (${closedTickets.length}) :**
${closedTickets.map(t => `- [${t.id}] ${t.title} (${t.type})`).join('\n')}
**Déploiements (${deployments.length}) :**
${deployments.map(d => `- ${d.service} → ${d.environment} (${d.status})`).join('\n')}
Format : résumé exécutif (3 phrases), travail accompli, incidents / problèmes, plan semaine suivante.`,
},
},
],
}
}
throw new McpError(ErrorCode.MethodNotFound, `Prompt inconnu : ${name}`)
})Un prompt peut contenir plusieurs messages pour simuler un historique de conversation :
return {
messages: [
{
role: 'user',
content: {
type: 'text',
text: 'Tu es un expert en sécurité web. Je vais te montrer du code à auditer.',
},
},
{
role: 'assistant',
content: {
type: 'text',
text: 'Je suis prêt. Montre-moi le code à analyser.',
},
},
{
role: 'user',
content: {
type: 'text',
text: `Voici le code à auditer :\n\n\`\`\`${code}\`\`\``,
},
},
],
}Le faux échange "user/assistant" au début configure le comportement de Claude sans que ce soit visible comme une instruction système.
Un prompt peut référencer une resource du même serveur :
return {
messages: [
{
role: 'user',
content: {
type: 'resource',
resource: {
uri: `db://users/${userId}`,
mimeType: 'application/json',
text: JSON.stringify(await getUserById(userId), null, 2),
},
},
},
{
role: 'user',
content: {
type: 'text',
text: 'Analyse le profil de cet utilisateur et identifie les données manquantes ou incohérentes.',
},
},
],
}Les clients MCP qui supportent les prompts peuvent les instancier via une commande. En pratique, depuis Claude Code :
> utilise le prompt analyze_error avec l'erreur ERR-2026-0624-001Claude récupère le prompt, les messages sont envoyés, Claude analyse et répond.
Tools, resources, prompts — les trois primitives MCP sont couvertes. Le chapitre suivant traite des transports : comment exposer votre serveur via stdio (local) ou HTTP (remote), et comment déployer en production.