iducationducation
IndexArticlesFormationsProfilOutilsBibliothech
N°014 — 2026
Navigation
01Index02Articles03Formations04Profil05Outils06Bibliothech
N°014 — 2026
iducationducation
IndexArticlesFormationsProfilOutilsBibliothech
N°014 — 2026
Navigation
01Index02Articles03Formations04Profil05Outils06Bibliothech
N°014 — 2026
Formations
Node.js · Intermédiaire

Créer un serveur MCP en Node.js

Construire un serveur MCP de A à Z : tools, resources, prompts, validation, transports stdio et HTTP — pour connecter Claude à n'importe quel système externe.

Node.jsTypeScriptMCPClaude Code
01Comprendre MCP02Premier serveur MCP03Tools avancés — validation, erreurs, structure04Resources — exposer du contenu05Prompts — templates réutilisables06Transports et déploiement07Projet complet — MCP de gestion de projet
Chapitre 2·20 min

Premier serveur MCP

On part de zéro. À la fin de ce chapitre, Claude Code sera connecté à votre serveur et pourra appeler votre premier outil.

Setup du projet

mkdir mon-mcp && cd mon-mcp
npm init -y

Dépendances

npm install @modelcontextprotocol/sdk zod
npm install -D typescript tsx @types/node
  • @modelcontextprotocol/sdk — le SDK officiel MCP
  • zod — validation de schéma (indispensable en pratique)
  • tsx — exécute du TypeScript directement sans build

tsconfig.json

{
  "compilerOptions": {
    "target": "ES2022",
    "module": "Node16",
    "moduleResolution": "Node16",
    "outDir": "./dist",
    "rootDir": "./src",
    "strict": true,
    "esModuleInterop": true
  },
  "include": ["src/**/*"]
}

package.json

{
  "name": "mon-mcp",
  "version": "1.0.0",
  "type": "module",
  "scripts": {
    "dev": "tsx src/index.ts",
    "build": "tsc",
    "start": "node dist/index.js"
  }
}

"type": "module" — on écrit en ESM. Le SDK MCP utilise ESM, votre code aussi.

Structure du projet

mon-mcp/
├── src/
│   ├── index.ts       ← point d'entrée, setup serveur + transport
│   ├── tools/
│   │   └── index.ts   ← définition des tools
│   └── resources/
│       └── index.ts   ← définition des resources
├── package.json
└── tsconfig.json

Premier serveur — squelette minimal

// src/index.ts
import { Server } from '@modelcontextprotocol/sdk/server/index.js'
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
import {
  CallToolRequestSchema,
  ListToolsRequestSchema,
} from '@modelcontextprotocol/sdk/types.js'
 
// ── Créer le serveur ──────────────────────────────────────────────────
const server = new Server(
  {
    name: 'mon-mcp',       // identifiant du serveur
    version: '1.0.0',
  },
  {
    capabilities: {
      tools: {},           // ce serveur expose des tools
    },
  }
)
 
// ── Déclarer les tools disponibles ───────────────────────────────────
server.setRequestHandler(ListToolsRequestSchema, async () => ({
  tools: [
    {
      name: 'add',
      description: 'Additionne deux nombres',
      inputSchema: {
        type: 'object',
        properties: {
          a: { type: 'number', description: 'Premier nombre' },
          b: { type: 'number', description: 'Deuxième nombre' },
        },
        required: ['a', 'b'],
      },
    },
  ],
}))
 
// ── Implémenter les tools ─────────────────────────────────────────────
server.setRequestHandler(CallToolRequestSchema, async (request) => {
  const { name, arguments: args } = request.params
 
  if (name === 'add') {
    const a = args?.a as number
    const b = args?.b as number
    return {
      content: [
        {
          type: 'text',
          text: `${a} + ${b} = ${a + b}`,
        },
      ],
    }
  }
 
  throw new Error(`Tool inconnu : ${name}`)
})
 
// ── Démarrer avec le transport stdio ──────────────────────────────────
const transport = new StdioServerTransport()
await server.connect(transport)

Tester le serveur manuellement

Avant de connecter Claude, tester que le serveur répond :

# Envoyer une requête JSON-RPC manuellement
echo '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}' | npx tsx src/index.ts

Réponse attendue :

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "tools": [
      {
        "name": "add",
        "description": "Additionne deux nombres",
        "inputSchema": { ... }
      }
    ]
  }
}

Si vous voyez ça, le serveur fonctionne.

Connecter à Claude Code

Éditer ~/.claude/settings.json (ou .claude/settings.json pour le projet) :

{
  "mcpServers": {
    "mon-mcp": {
      "command": "npx",
      "args": ["tsx", "/chemin/absolu/vers/mon-mcp/src/index.ts"]
    }
  }
}

Ou avec le build compilé :

{
  "mcpServers": {
    "mon-mcp": {
      "command": "node",
      "args": ["/chemin/absolu/vers/mon-mcp/dist/index.js"]
    }
  }
}

Redémarrer Claude Code. Au démarrage, il lance le processus npx tsx ... et communique via stdio.

Vérifier la connexion

Dans Claude Code :

> liste les outils MCP disponibles

Claude doit mentionner add parmi les outils. Puis :

> utilise l'outil add avec 17 et 25

Claude appelle le tool et retourne 17 + 25 = 42.

Ajouter un tool plus utile

Un tool qui requête une API externe :

// Dans ListToolsRequestSchema handler, ajouter dans le tableau tools :
{
  name: 'get_weather',
  description: 'Retourne la météo actuelle pour une ville',
  inputSchema: {
    type: 'object',
    properties: {
      city: {
        type: 'string',
        description: 'Nom de la ville (ex: Paris, Lyon)',
      },
    },
    required: ['city'],
  },
},
 
// Dans CallToolRequestSchema handler :
if (name === 'get_weather') {
  const city = args?.city as string
  const res = await fetch(
    `https://wttr.in/${encodeURIComponent(city)}?format=j1`
  )
  const data = await res.json() as any
  const current = data.current_condition[0]
 
  return {
    content: [
      {
        type: 'text',
        text: JSON.stringify({
          city,
          temp_c: current.temp_C,
          feels_like_c: current.FeelsLikeC,
          description: current.weatherDesc[0].value,
          humidity: current.humidity,
        }, null, 2),
      },
    ],
  }
}
> quel temps fait-il à Bordeaux ?

Claude appelle get_weather({ city: "Bordeaux" }) et interprète les données retournées.

Pattern : retourner plusieurs content items

Un tool peut retourner plusieurs blocs de contenu :

return {
  content: [
    {
      type: 'text',
      text: '## Résultats de la requête',
    },
    {
      type: 'text',
      text: JSON.stringify(results, null, 2),
    },
    {
      type: 'text',
      text: `${results.length} résultats trouvés.`,
    },
  ],
}

Types de content disponibles : text, image (base64), resource (référence à une resource MCP).

Logging sans polluer stdio

Le transport stdio utilise stdin/stdout pour le protocole. console.log sur stdout casse le protocole. Pour logger :

// Toujours sur stderr
console.error('[mon-mcp] serveur démarré')
console.error('[mon-mcp] tool appelé :', name, args)

Ou utiliser server.sendLoggingMessage() :

await server.sendLoggingMessage({
  level: 'info',
  data: `Tool ${name} appelé avec ${JSON.stringify(args)}`,
})

Les logs MCP apparaissent dans les logs de Claude Code sans polluer la communication.


Le serveur tourne, Claude s'y connecte et appelle les tools. Le chapitre suivant rend les tools robustes : validation typée avec Zod, gestion d'erreurs structurée, tools asynchrones complexes.

Précédent
Comprendre MCP
Suivant
Tools avancés — validation, erreurs, structure

Développeur fullstack passionné. J'apprends en construisant et je documente tout — front, back, outils. Le code s'apprend mieux en public.

Naviguer

IndexTous les articlesFormationsProfilOutilsBibliothech

Ailleurs

GitHub RSS

Newsletter

Les articles, libs et découvertes. Une fois par semaine, pas plus.

© 2026 William LoreeConçu & codé à la main