+200 XP

Créer votre propre serveur MCP

Un connecteur n'est rien d'autre qu'un serveur qui parle un protocole, et dans les trente minutes qui viennent vous allez en écrire un qui donne à Claude un tool `get_order_status` qu'il peut appeler pour vous. Nous le ferons tourner en local d'abord, puis nous l'exposerons comme connecteur distant, puis nous dirons un mot prudent sur qui a le droit de l'appeler.

Vous connaissez déjà MCP (le Model Context Protocol) comme le standard ouvert qui permet à Claude d'atteindre des tools et des données en dehors de sa fenêtre de contexte. Vous allez maintenant construire l'autre côté de cette poignée de main : le serveur.

Ce qu'est réellement un serveur MCP

Un serveur MCP est un petit programme qui annonce une liste de capacités et attend qu'un client les appelle. Les trois types de capacités sont les tools (des fonctions que le modèle peut invoquer), les resources (des données en lecture seule que le modèle peut récupérer) et les prompts (des templates réutilisables). Pour un connecteur qui répond à « où est ma commande », vous voulez un tool.

Le client est l'application hôte : Claude Desktop, les apps Claude, Claude Code, ou votre propre code utilisant l'Agent SDK. **Le client décide *quand* appeler votre tool. Votre serveur décide seulement *ce que fait le tool***. Cette séparation est tout l'enjeu. Vous ne touchez jamais au modèle. Vous publiez une signature de fonction propre et une description, et Claude détermine quand l'appeler est utile.

Deux transports comptent :

  • stdio : le serveur tourne comme un sous-processus local et communique via l'entrée/sortie standard. C'est ainsi que Claude Desktop lance un connecteur local. Zéro réseau, zéro auth, le plus rapide à construire.
  • Streamable HTTP : le serveur tourne comme un service web à une URL. C'est ainsi que fonctionne un connecteur *distant*, et c'est ce que vous soumettez au marketplace de connecteurs ou partagez avec une équipe.

Vous écrivez la logique du tool une seule fois. Le transport, c'est quelques lignes en bas du fichier. Commencez par stdio.

Le serveur minimal

Installez d'abord le SDK Python officiel. Le package `mcp` embarque un helper `FastMCP` qui gère la plomberie du protocole, si bien que vous n'écrivez presque rien d'autre que votre propre fonction.

bash
pip install "mcp[cli]"

Maintenant le serveur. C'est tout.

python
from mcp.server.fastmcp import FastMCP

mcp = FastMCP("order-tools")

# Un petit substitut à votre vraie base de données ou API interne.
ORDERS = {
    "A1001": {"status": "shipped", "carrier": "DHL", "eta": "2026-02-14"},
    "A1002": {"status": "processing", "carrier": None, "eta": None},
}

@mcp.tool()
def get_order_status(order_id: str) -> dict:
    """Look up the current status of a customer order by its ID.

    Args:
        order_id: The order reference, e.g. 'A1001'.
    """
    order = ORDERS.get(order_id.strip().upper())
    if order is None:
        return {"found": False, "order_id": order_id}
    return {"found": True, "order_id": order_id, **order}

if __name__ == "__main__":
    mcp.run()

Lisez-le de haut en bas :

  • FastMCP("order-tools") crée le serveur et le nomme. Ce nom apparaît dans l'interface du client.
  • Le décorateur @mcp.tool() enregistre la fonction comme un tool appelable. Le SDK lit vos type hints (order_id: str, retour dict) pour construire automatiquement le schéma d'entrée et de sortie. Pas de JSON Schema à la main.
  • La docstring n'est pas de la décoration. Claude la lit pour décider quand et comment appeler le tool. La première ligne décrit le tool ; la section Args: documente chaque paramètre. Écrivez-la comme si vous briefiez un nouveau collègue intelligent qui ne peut pas voir votre code.
  • mcp.run() sans argument utilise par défaut le transport stdio. C'est tout ce dont vous avez besoin en local.

Dans la vraie version, le corps de get_order_status appelle votre système de commandes : une requête SQL, un endpoint REST interne, une recherche Stripe. La couche MCP ne change jamais. Vous encapsulez une capacité existante, vous ne la reconstruisez pas.

Le faire tourner en local dans Claude Desktop

Claude Desktop lit un petit fichier de configuration qui liste les serveurs locaux qu'il doit lancer. Sur macOS il se trouve dans ~/Library/Application Support/Claude/claude_desktop_config.json. Ajoutez votre serveur :

json
{
  "mcpServers": {
    "order-tools": {
      "command": "python",
      "args": ["/absolute/path/to/server.py"]
    }
  }
}

Redémarrez Claude Desktop. L'app lance votre script comme sous-processus, lui demande sa liste de tools, et affiche order-tools dans le menu des connecteurs. Tapez maintenant : *« Quel est le statut de la commande A1001 ? »* Claude voit le tool, appelle get_order_status("A1001"), récupère le JSON et répond en langage naturel. On vous demandera d'approuver l'appel la première fois. Cette étape d'approbation, c'est le client qui vous protège, et c'est délibéré.

Si rien n'apparaît, lancez d'abord python /path/to/server.py dans un terminal pour repérer les erreurs d'import, puis utilisez le MCP Inspector (mcp dev server.py) pour tester le tool directement avant d'impliquer Claude. Déboguez le serveur isolément ; déboguez la connexion ensuite.

Pour le guide officiel et les derniers détails du SDK, gardez le quickstart serveur MCP ouvert dans un onglet. Le protocole évolue, et cette page est la source de vérité.

Build an MCP Server in Python

Watch on YouTube

Passer en distant : du sous-processus au connecteur

Un serveur stdio local ne sert qu'à la personne qui le fait tourner sur sa propre machine. Pour qu'une *équipe* utilise votre connecteur, ou pour le lister dans le marketplace de connecteurs, il doit tourner comme un service HTTP distant à une URL.

Le changement de code est quasi nul. Changez le transport :

python
if __name__ == "__main__":
    mcp.run(transport="streamable-http")

Votre serveur écoute maintenant sur un endpoint HTTP au lieu de stdio. Déployez-le comme n'importe quel service web : un conteneur sur le cloud de votre choix, derrière HTTPS, sur un hostname stable tel que https://tools.yourco.com/mcp. La documentation d'Anthropic couvre les exigences des connecteurs distants, dont le transport Streamable HTTP attendu par les apps Claude.

Dans les apps Claude, un utilisateur (ou un admin, pour une organisation) ajoute votre connecteur par URL dans Settings, puis Connectors. À partir de cet instant votre tool apparaît aux côtés des tools intégrés dans les Projects, dans les conversations classiques, et pour les agents gérés. Un serveur, plusieurs surfaces.

Le plus dur dans le passage en distant n'est pas le transport. C'est la question que la version stdio vous laissait ignorer : qui appelle, et qu'a-t-il le droit de voir ?

Vérification des acquis

1. Dans l'architecture MCP décrite, quelle est la répartition des responsabilités entre le client et votre serveur ?

2. Pour répondre à une question comme « où est ma commande », quel type de capacité MCP est le bon choix, et pourquoi ?

3. Pourquoi la leçon recommande-t-elle de commencer par le transport stdio avant de passer à Streamable HTTP ?

CHOIX MULTIPLES

4. Sélectionnez TOUTES les affirmations qui décrivent correctement les transports stdio et Streamable HTTP.

Sélectionnez toutes les réponses correctes.

CHOIX MULTIPLES

5. Sélectionnez TOUTES les affirmations correctes sur les serveurs MCP et le helper FastMCP.

Sélectionnez toutes les réponses correctes.

Un mot sur l'authentification

Dès que votre serveur est joignable sur internet, get_order_status("A1001") devient un problème. La commande A1001 appartient à *quelqu'un*. Sans auth, quiconque trouve votre URL peut énumérer toutes les commandes. Les serveurs stdio locaux héritent de la confiance de la machine sur laquelle ils tournent. Les serveurs distants n'héritent de rien. Vous devez l'ajouter.

Le transport distant de MCP prend en charge OAuth 2.1 précisément pour cela. Le flow, en clair :

  1. Un utilisateur ajoute votre connecteur dans l'app Claude.
  2. Avant que le connecteur ne fonctionne, l'app envoie l'utilisateur vers votre serveur d'autorisation pour se connecter et consentir.
  3. Votre serveur émet un token d'accès lié à *cet utilisateur précis*.
  4. Chaque appel de tool venant de Claude arrive désormais porteur de ce token.

Dans votre tool, vous lisez le token, vous le résolvez en utilisateur, et vous scopez la requête. Le même appel get_order_status renvoie des lignes différentes selon qui demande :

python
@mcp.tool()
def get_order_status(order_id: str, ctx: Context) -> dict:
    user = resolve_user(ctx.request_context)   # depuis le bearer token
    order = lookup_order(order_id, owner=user.id)
    if order is None:
        return {"found": False, "order_id": order_id}
    return {"found": True, **order}

Le principe : ne faites jamais confiance au seul `order_id`. Faites confiance à l'identité authentifiée, puis vérifiez que cette identité a le droit de voir cette commande. Le modèle n'est pas votre frontière de sécurité. Votre serveur l'est. Claude transmettra volontiers tout ce que l'utilisateur tape, y compris un order ID qui appartient à quelqu'un d'autre, donc le contrôle de propriété vit dans votre code et nulle part ailleurs.

Deux remarques pratiques pour 2025-2026 :

  • Pour des tools purement internes, un simple bearer token ou une clé d'API passée en header est acceptable, à condition que le transport soit en HTTPS et que le token corresponde à un principal réel sur lequel vous pouvez scoper. OAuth complet est pour les connecteurs que de vrais utilisateurs ajoutent eux-mêmes.
  • Si vous listez un connecteur dans le marketplace ou le partagez à l'échelle d'une organisation, suivez les exigences d'Anthropic en matière de connecteurs et de sécurité. Les admins d'org contrôlent quels connecteurs sont activés, et cette couche de gouvernance suppose que votre serveur authentifie correctement en dessous. Voir la documentation connecteurs d'Anthropic pour le niveau d'exigence actuel.

Où cela s'inscrit dans l'écosystème

Votre serveur MCP est une brique, pas l'application entière. Le serveur que vous venez d'écrire se branche sur plusieurs surfaces Anthropic sans aucune modification :

  • Claude Code peut le charger pour que l'agent de code appelle get_order_status pendant qu'il travaille dans votre repo.
  • Le Claude Agent SDK vous permet de construire un agent géré qui utilise votre connecteur comme l'un de plusieurs tools, aux côtés de l'accès aux fichiers et de l'intégration GitHub.
  • Le marketplace de connecteurs permet à d'autres de le découvrir et de l'ajouter.

Comparez cela aux Skills, qui packagent des instructions, des scripts et des fichiers façonnant la manière dont Claude *se comporte* sur une tâche. Un Skill apprend à Claude une procédure. Un serveur MCP donne à Claude une *capacité* qu'il n'avait pas : l'accès en direct à votre système de commandes. Vous les associerez souvent. Un Skill « support client » qui connaît votre ton et vos règles d'escalade, appelant un serveur MCP order-tools pour les données en direct. Savoir quel problème chacun résout, c'est la moitié du travail pour bien construire sur Claude.

Construisez le tool une fois. Choisissez son transport selon l'audience. Protégez-le par l'identité. Toute la discipline est là.

Points clés à retenir

  • Commencez en stdio, livrez en HTTP. Écrivez et déboguez votre tool comme serveur stdio local avec FastMCP, puis changez une ligne (transport="streamable-http") pour en faire un connecteur distant. La logique du tool ne change jamais.
  • La docstring et les type hints sont l'interface. Claude décide quand appeler votre tool à partir de son nom, de sa description et de ses paramètres. Écrivez-les avec autant de soin que le code.
  • Authentifiez dès le passage en distant. Utilisez OAuth 2.1 pour les connecteurs destinés aux utilisateurs, scopez chaque requête sur l'identité authentifiée, et ne laissez jamais l'input du modèle devenir votre frontière de sécurité.
  • Testez d'abord en isolation. Utilisez mcp dev server.py et le MCP Inspector pour vérifier que le tool fonctionne avant de le brancher dans Claude Desktop ou les apps.
  • Choisissez la bonne primitive. Les serveurs MCP ajoutent des *capacités* (données en direct, actions) ; les Skills façonnent le *comportement* (procédures, ton). Les vrais connecteurs combinent généralement les deux.

À faire, tiré de cette leçon

Ces actions sont compilées dans le plan d'action du rôle.

  • Rédiger les docstrings et les type hints des tools pour le routing du modèle
  • Développez les serveurs MCP en stdio local, puis basculez le transport en remote
  • Restreindre chaque requête MCP distante à l'identité authentifiée
Voir le plan d'action complet →

Articles liés

Les articles récents du blog qui s'appuient sur cette leçon.