Building MCP Server with Python

Créer un serveur MCP en Python : guide + Inspector

event

La promesse des assistants IA a toujours reposé sur leur capacité à aider dans des tâches concrètes, mais il existait une limite fondamentale : la plupart des systèmes d'IA fonctionnent de manière isolée, incapables d'accéder aux sources de données, aux API et aux outils spécifiques qui les rendent réellement utiles pour votre cas d'usage particulier. Que vous ayez besoin qu'une IA interroge la base de données de votre entreprise, interagisse avec votre système de gestion de projet ou accède à des données en temps réel provenant d'API spécialisées, l'approche traditionnelle a toujours exigé la création d'intégrations sur mesure pour chaque plateforme d'IA - un processus chronophage et fragmenté.

C'est là que le Model Context Protocol (MCP) change tout. Au lieu que les assistants IA soient limités à leurs données d'entraînement ou nécessitent des intégrations personnalisées complexes, MCP offre un moyen standardisé de connecter les systèmes d'IA à vos sources de données et outils spécifiques. Vous avez besoin que votre IA accède à votre base de données clients ? Créez un serveur MCP. Vous voulez qu'elle interagisse avec votre API de gestion des stocks ? Créez un serveur MCP. Le même serveur fonctionne sur différentes plateformes d'IA, éliminant la nécessité de reconstruire les intégrations pour chaque système.

L'adoption de MCP s'accélère rapidement dans l'ensemble de l'écosystème de l'IA. Les grandes plateformes adoptent le protocole comme standard d'intégration de l'IA. Claude Desktop et Claude for Code offrent déjà une prise en charge native de MCP, permettant aux utilisateurs de se connecter en toute fluidité à des sources de données et outils personnalisés. Les principaux fournisseurs d'API d'IA - OpenAI, Anthropic et Google - ajoutent la compatibilité MCP à leurs API de complétion, permettant aux développeurs de créer des applications d'IA capables d'accéder à des systèmes externes via des interfaces standardisées. Cet écosystème en pleine croissance signifie que les serveurs MCP que vous construisez aujourd'hui fonctionneront avec une gamme sans cesse plus large de plateformes et d'applications d'IA.

Dans ce tutoriel complet, vous apprendrez à créer des serveurs MCP prêts pour la production à l'aide de Python, selon une approche itérative et pratique. Nous commencerons par un serveur minimal et ajouterons progressivement des fonctionnalités, en vous montrant exactement comment développer, tester et étendre votre serveur étape par étape. À la fin de ce guide, vous aurez construit un serveur MCP de service météorologique complet, doté de fonctionnalités avancées telles que le sampling assisté par IA, l'authentification OAuth et des capacités de déploiement en production.

Comprendre MCP

Le Model Context Protocol représente un changement de paradigme dans notre façon de concevoir l'architecture d'intégration de l'IA. Fondamentalement, MCP est un protocole ouvert qui standardise la manière dont les applications fournissent du contexte aux grands modèles de langage, mais ses implications vont bien au-delà du simple partage de données.

Pour comprendre pourquoi MCP est important, considérez l'état actuel des intégrations d'IA. La plupart des applications d'IA fonctionnent aujourd'hui de manière isolée, avec une capacité limitée à accéder aux données en temps réel ou à interagir avec des systèmes externes. Lorsque les développeurs souhaitent donner à un assistant IA l'accès à une base de données, un système de fichiers ou un service web, ils doivent généralement créer des solutions sur mesure fortement couplées à des plateformes d'IA spécifiques. Cette approche engendre plusieurs problèmes : la dépendance vis-à-vis d'un fournisseur, la duplication des efforts entre différentes plateformes d'IA, des préoccupations de sécurité liées à l'accès direct aux API, et une charge de maintenance à mesure que les API évoluent.

MCP répond à ces défis grâce à une architecture client-serveur qui introduit une couche intermédiaire standardisée. Au lieu que les applications d'IA accèdent directement aux systèmes externes, elles communiquent via des serveurs MCP qui font office de passerelles sécurisées et standardisées. Cette architecture offre plusieurs avantages clés qui la rendent particulièrement puissante pour les déploiements en entreprise et en production.

Architecture fondamentale de MCP

Au cœur de l'architecture de MCP se trouvent trois participants principaux qui collaborent pour permettre une intégration fluide de l'IA :

MCP Host : L'application d'IA qui coordonne et gère les connexions à plusieurs serveurs MCP. Des applications d'IA populaires comme Claude Desktop font office d'hôtes MCP lorsqu'elles prennent en charge le protocole.

MCP Client : Un composant au sein de l'hôte qui maintient des connexions dédiées à des serveurs MCP individuels, gérant la communication au niveau du protocole et le cycle de vie des connexions.

MCP Server : Le composant qui expose des données et des fonctionnalités aux clients MCP de manière standardisée. Les serveurs peuvent s'exécuter localement (via le transport STDIO) ou à distance (via le transport HTTP).

Les trois piliers de MCP

Le protocole définit trois primitives fondamentales que les serveurs peuvent exposer :

Les outils (Tools) sont des fonctions exécutables que les applications d'IA peuvent invoquer pour effectuer des actions. Il peut s'agir d'opérations telles que l'interrogation d'une base de données, l'envoi d'un e-mail ou l'appel d'une API externe.

Les ressources (Resources) fournissent des informations contextuelles aux applications d'IA sans effectuer d'action. Elles représentent des données qui peuvent être lues et comprises par l'IA, comme le contenu d'un fichier ou des enregistrements de base de données.

Les prompts sont des modèles réutilisables qui aident à structurer les interactions avec les modèles de langage, offrant un moyen d'encapsuler l'expertise métier et les bonnes pratiques.

Configurer votre environnement de développement

Avant de commencer à construire notre serveur MCP, mettons en place un environnement de développement adapté qui prend en charge à la fois l'itération rapide et le déploiement en production. Nous utiliserons le SDK Python du Model Context Protocol tout au long de ce tutoriel.

Prérequis

Assurez-vous d'avoir Python 3.10 ou une version supérieure installée sur votre système. Vous pouvez vérifier votre version de Python avec :

python --version
# or
python3 --version

Créer votre projet

Créez un nouveau répertoire de projet et configurez un environnement virtuel :

# Create project directory
mkdir weather-mcp-server
cd weather-mcp-server

# Create a virtual environment
python -m venv .venv

# Activate the virtual environment
# On macOS/Linux:
source .venv/bin/activate

# On Windows:
.venv\Scripts\activate

Installer les dépendances

Installez le SDK Python de MCP et les dépendances supplémentaires à l'aide de pip :

# Upgrade pip to the latest version
pip install --upgrade pip

# Install MCP SDK with CLI tools
pip install "mcp[cli]"

# Install HTTP client for API requests
pip install httpx

# Install JWT library for authentication (we'll use this later)
pip install PyJWT

# Install development dependencies
pip install pytest black isort mypy

Créer un fichier requirements

Créez un fichier requirements.txt pour suivre vos dépendances :

# Generate requirements file
pip freeze > requirements.txt

Votre fichier requirements.txt devrait inclure des entrées comme :

mcp[cli]
httpx
PyJWT
pytest
black
isort
mypy

Construire votre premier serveur MCP : étape par étape

Construisons maintenant notre serveur MCP météo de manière itérative, en commençant par l'implémentation la plus simple possible et en ajoutant des fonctionnalités étape par étape. Cette approche vous aide à comprendre chaque composant et facilite le débogage.

Étape 1 : Créer un serveur MCP minimal

Commençons par le strict minimum - un serveur qui ne fait rien d'autre que répondre aux messages de base du protocole MCP. Créez un fichier appelé weather_server.py :

"""
Step 1: Minimal MCP server that responds to protocol messages
"""
from mcp.server.fastmcp import FastMCP

# Create the MCP server instance
mcp = FastMCP("weather-server")

if __name__ == "__main__":
    # Run the server using STDIO transport
    mcp.run(transport='stdio')

Testez ce serveur minimal :

# Start the MCP Inspector to test your server
python -m mcp dev weather_server.py

Le MCP Inspector lancera une interface web (généralement à l'adresse http://localhost:3000) où vous pourrez constater que votre serveur fonctionne et répond aux messages du protocole MCP, même s'il n'expose encore aucun outil.

Étape 2 : Ajouter votre premier outil

Ajoutons maintenant un outil simple qui renvoie des informations météorologiques statiques :

"""
Step 2: Add a simple weather tool with static data
"""
from mcp.server.fastmcp import FastMCP

mcp = FastMCP("weather-server")

@mcp.tool()
def get_weather(city: str) -> str:
    """Get current weather for a city (demo with static data)"""
    # For now, return static data to test the tool mechanism
    return f"Weather in {city}: Sunny, 22°C (This is demo data)"

if __name__ == "__main__":
    mcp.run(transport='stdio')

Testez le nouvel outil dans le MCP Inspector. Vous devriez désormais voir un outil get_weather que vous pouvez appeler avec différents noms de villes.

Étape 3 : Ajouter une intégration à une API réelle

Connectons-nous maintenant à une véritable API météo. Nous utiliserons l'API du National Weather Service, qui est gratuite et ne nécessite pas d'authentification :

"""
Step 3: Connect to real weather API
"""
import httpx
from mcp.server.fastmcp import FastMCP

mcp = FastMCP("weather-server")

# Configuration
NWS_API_BASE = "https://api.weather.gov"
USER_AGENT = "weather-mcp-server/1.0"

async def make_nws_request(url: str) -> dict | None:
    """Make a request to the National Weather Service API"""
    headers = {
        "User-Agent": USER_AGENT,
        "Accept": "application/geo+json"
    }

    async with httpx.AsyncClient() as client:
        try:
            response = await client.get(url, headers=headers, timeout=30.0)
            response.raise_for_status()
            return response.json()
        except Exception as e:
            print(f"API request failed: {e}", file=sys.stderr)
            return None

@mcp.tool()
async def get_forecast(latitude: float, longitude: float) -> str:
    """Get weather forecast for a specific location using coordinates"""
    # Step 1: Get the forecast grid endpoint for this location
    points_url = f"{NWS_API_BASE}/points/{latitude},{longitude}"
    points_data = await make_nws_request(points_url)

    if not points_data:
        return f"Unable to fetch forecast data for coordinates ({latitude}, {longitude})"

    # Step 2: Get the actual forecast data
    properties = points_data.get("properties", {})
    forecast_url = properties.get("forecast")

    if not forecast_url:
        return "Error: Unable to determine forecast URL for this location"

    forecast_data = await make_nws_request(forecast_url)

    if not forecast_data:
        return "Unable to fetch detailed forecast data"

    # Format the first few periods
    periods = forecast_data.get("properties", {}).get("periods", [])

    if not periods:
        return "No forecast periods available for this location"

    # Format the first 3 periods for display
    forecasts = []
    for period in periods[:3]:
        forecast_text = f"""
{period.get('name', 'Unknown Period')}:
Temperature: {period.get('temperature', 'Unknown')}°{period.get('temperatureUnit', 'F')}
Wind: {period.get('windSpeed', 'Unknown')} {period.get('windDirection', '')}
Forecast: {period.get('detailedForecast', 'No detailed forecast available')}
"""
        forecasts.append(forecast_text.strip())

    return f"Forecast for {latitude}, {longitude}:\n" + "\n---\n".join(forecasts)

if __name__ == "__main__":
    import sys
    mcp.run(transport='stdio')

Testez cette version avec des coordonnées réelles (par exemple, New York : 40.7128, -74.0060). Vous devriez maintenant obtenir de véritables données de prévisions météorologiques !

Étape 4 : Ajouter la validation des entrées et la gestion des erreurs

Rendons notre serveur plus robuste en ajoutant une validation des entrées et une gestion des erreurs appropriées :

"""
Step 4: Add input validation and better error handling
"""
import sys
import httpx
from mcp.server.fastmcp import FastMCP

mcp = FastMCP("weather-server")

# Configuration
NWS_API_BASE = "https://api.weather.gov"
USER_AGENT = "weather-mcp-server/1.0"

async def make_nws_request(url: str) -> dict | None:
    """Make a request to the National Weather Service API with proper error handling"""
    headers = {
        "User-Agent": USER_AGENT,
        "Accept": "application/geo+json"
    }

    async with httpx.AsyncClient() as client:
        try:
            response = await client.get(url, headers=headers, timeout=30.0)
            response.raise_for_status()
            return response.json()
        except httpx.TimeoutException:
            print(f"Request timeout for URL: {url}", file=sys.stderr)
            return None
        except httpx.HTTPStatusError as e:
            print(f"HTTP error {e.response.status_code} for URL: {url}", file=sys.stderr)
            return None
        except Exception as e:
            print(f"Unexpected error for URL {url}: {e}", file=sys.stderr)
            return None

@mcp.tool()
async def get_forecast(latitude: float, longitude: float) -> str:
    """Get weather forecast for a specific location using coordinates"""
    # Validate coordinate ranges
    if not (-90 <= latitude <= 90):
        return "Error: Latitude must be between -90 and 90 degrees"

    if not (-180 <= longitude <= 180):
        return "Error: Longitude must be between -180 and 180 degrees"

    # Step 1: Get the forecast grid endpoint for this location
    points_url = f"{NWS_API_BASE}/points/{latitude},{longitude}"
    points_data = await make_nws_request(points_url)

    if not points_data:
        return f"Unable to fetch forecast data for coordinates ({latitude}, {longitude}). This location may be outside the US or the service may be unavailable."

    # Extract the forecast URL from the points response
    properties = points_data.get("properties", {})
    forecast_url = properties.get("forecast")

    if not forecast_url:
        return "Error: Unable to determine forecast URL for this location"

    # Step 2: Get the actual forecast data
    forecast_data = await make_nws_request(forecast_url)

    if not forecast_data:
        return "Unable to fetch detailed forecast data"

    # Extract and format forecast periods
    forecast_properties = forecast_data.get("properties", {})
    periods = forecast_properties.get("periods", [])

    if not periods:
        return "No forecast periods available for this location"

    # Format the first 3 periods for display
    forecasts = []
    for period in periods[:3]:
        forecast_text = f"""
{period.get('name', 'Unknown Period')}:
Temperature: {period.get('temperature', 'Unknown')}°{period.get('temperatureUnit', 'F')}
Wind: {period.get('windSpeed', 'Unknown')} {period.get('windDirection', '')}
Forecast: {period.get('detailedForecast', 'No detailed forecast available')}
"""
        forecasts.append(forecast_text.strip())

    location_info = f"Forecast for {latitude}, {longitude}:\n"
    return location_info + "\n---\n".join(forecasts)

@mcp.tool()
async def get_alerts(state: str) -> str:
    """Get active weather alerts for a US state"""
    # Validate state code format
    if not state or len(state) != 2:
        return "Error: Please provide a valid two-letter US state code (e.g., 'CA', 'NY', 'TX')"

    state = state.upper()
    url = f"{NWS_API_BASE}/alerts/active/area/{state}"

    data = await make_nws_request(url)

    if not data:
        return f"Unable to fetch weather alerts for {state}. The service may be temporarily unavailable."

    features = data.get("features", [])

    if not features:
        return f"No active weather alerts for {state}."

    # Format alerts for display
    alerts = []
    for feature in features:
        props = feature.get("properties", {})
        event = props.get('event', 'Unknown Event')
        area = props.get('areaDesc', 'Unknown Area')
        severity = props.get('severity', 'Unknown Severity')
        description = props.get('description', 'No description available')

        alert_text = f"""
Event: {event}
Area: {area}
Severity: {severity}
Description: {description[:200]}{'...' if len(description) > 200 else ''}
"""
        alerts.append(alert_text.strip())

    alert_count = len(alerts)
    header = f"Found {alert_count} active weather alert{'s' if alert_count != 1 else ''} for {state}:\n"
    return header + "\n---\n".join(alerts)

if __name__ == "__main__":
    mcp.run(transport='stdio')

Testez maintenant les deux outils avec diverses entrées, y compris des entrées invalides, pour voir comment fonctionne la gestion des erreurs.

Étape 5 : Ajouter des ressources pour les informations contextuelles

Ajoutons des ressources qui fournissent des informations contextuelles sur les stations et les zones météorologiques :

"""
Step 5: Add resources for contextual weather information
"""
import sys
import httpx
from mcp.server.fastmcp import FastMCP

mcp = FastMCP("weather-server")

# Configuration
NWS_API_BASE = "https://api.weather.gov"
USER_AGENT = "weather-mcp-server/1.0"

async def make_nws_request(url: str) -> dict | None:
    """Make a request to the National Weather Service API with proper error handling"""
    headers = {
        "User-Agent": USER_AGENT,
        "Accept": "application/geo+json"
    }

    async with httpx.AsyncClient() as client:
        try:
            response = await client.get(url, headers=headers, timeout=30.0)
            response.raise_for_status()
            return response.json()
        except httpx.TimeoutException:
            print(f"Request timeout for URL: {url}", file=sys.stderr)
            return None
        except httpx.HTTPStatusError as e:
            print(f"HTTP error {e.response.status_code} for URL: {url}", file=sys.stderr)
            return None
        except Exception as e:
            print(f"Unexpected error for URL {url}: {e}", file=sys.stderr)
            return None

# Tools (same as Step 4)
@mcp.tool()
async def get_forecast(latitude: float, longitude: float) -> str:
    """Get weather forecast for a specific location using coordinates"""
    # Validate coordinate ranges
    if not (-90 <= latitude <= 90):
        return "Error: Latitude must be between -90 and 90 degrees"

    if not (-180 <= longitude <= 180):
        return "Error: Longitude must be between -180 and 180 degrees"

    # Step 1: Get the forecast grid endpoint for this location
    points_url = f"{NWS_API_BASE}/points/{latitude},{longitude}"
    points_data = await make_nws_request(points_url)

    if not points_data:
        return f"Unable to fetch forecast data for coordinates ({latitude}, {longitude}). This location may be outside the US or the service may be unavailable."

    # Extract the forecast URL from the points response
    properties = points_data.get("properties", {})
    forecast_url = properties.get("forecast")

    if not forecast_url:
        return "Error: Unable to determine forecast URL for this location"

    # Step 2: Get the actual forecast data
    forecast_data = await make_nws_request(forecast_url)

    if not forecast_data:
        return "Unable to fetch detailed forecast data"

    # Extract and format forecast periods
    forecast_properties = forecast_data.get("properties", {})
    periods = forecast_properties.get("periods", [])

    if not periods:
        return "No forecast periods available for this location"

    # Format the first 3 periods for display
    forecasts = []
    for period in periods[:3]:
        forecast_text = f"""
{period.get('name', 'Unknown Period')}:
Temperature: {period.get('temperature', 'Unknown')}°{period.get('temperatureUnit', 'F')}
Wind: {period.get('windSpeed', 'Unknown')} {period.get('windDirection', '')}
Forecast: {period.get('detailedForecast', 'No detailed forecast available')}
"""
        forecasts.append(forecast_text.strip())

    location_info = f"Forecast for {latitude}, {longitude}:\n"
    return location_info + "\n---\n".join(forecasts)

@mcp.tool()
async def get_alerts(state: str) -> str:
    """Get active weather alerts for a US state"""
    # Validate state code format
    if not state or len(state) != 2:
        return "Error: Please provide a valid two-letter US state code (e.g., 'CA', 'NY', 'TX')"

    state = state.upper()
    url = f"{NWS_API_BASE}/alerts/active/area/{state}"

    data = await make_nws_request(url)

    if not data:
        return f"Unable to fetch weather alerts for {state}. The service may be temporarily unavailable."

    features = data.get("features", [])

    if not features:
        return f"No active weather alerts for {state}."

    # Format alerts for display
    alerts = []
    for feature in features:
        props = feature.get("properties", {})
        event = props.get('event', 'Unknown Event')
        area = props.get('areaDesc', 'Unknown Area')
        severity = props.get('severity', 'Unknown Severity')
        description = props.get('description', 'No description available')

        alert_text = f"""
Event: {event}
Area: {area}
Severity: {severity}
Description: {description[:200]}{'...' if len(description) > 200 else ''}
"""
        alerts.append(alert_text.strip())

    alert_count = len(alerts)
    header = f"Found {alert_count} active weather alert{'s' if alert_count != 1 else ''} for {state}:\n"
    return header + "\n---\n".join(alerts)

# Resources for contextual information
@mcp.resource("weather://stations/{state}")
async def get_weather_stations(state: str) -> str:
    """Get information about weather observation stations in a state"""
    if not state or len(state) != 2:
        return "Error: Please provide a valid two-letter US state code"

    state = state.upper()
    url = f"{NWS_API_BASE}/stations?state={state}"

    data = await make_nws_request(url)

    if not data:
        return f"Unable to fetch weather station information for {state}"

    features = data.get("features", [])

    if not features:
        return f"No weather stations found for {state}"

    stations = []
    for feature in features[:10]:  # Limit to first 10 stations
        props = feature.get("properties", {})
        name = props.get("name", "Unknown Station")
        identifier = props.get("stationIdentifier", "Unknown ID")
        elevation = props.get("elevation", {}).get("value", "Unknown")

        stations.append(f"- {name} ({identifier}) - Elevation: {elevation}m")

    station_count = len(features)
    header = f"Weather stations in {state} (showing first 10 of {station_count}):\n"
    return header + "\n".join(stations)

if __name__ == "__main__":
    mcp.run(transport='stdio')

Dans le MCP Inspector, vous devriez maintenant voir à la fois les outils et les ressources. Les ressources apparaissent dans une section distincte et fournissent des informations contextuelles que les applications d'IA peuvent utiliser pour mieux comprendre les données météorologiques.

Tester votre serveur avec le MCP Inspector

Avant d'intégrer votre serveur à des assistants IA comme Claude Desktop, il est essentiel de le tester en profondeur à l'aide du MCP Inspector. L'Inspector fournit une interface web pour tester les serveurs MCP, vous permettant de vérifier que tous les outils et ressources fonctionnent correctement.

Démarrer le MCP Inspector

Pour tester votre serveur météo avec le MCP Inspector, exécutez la commande suivante depuis le répertoire de votre projet :

# Make sure you're in your project directory and virtual environment is activated
cd weather-mcp-server
source .venv/bin/activate  # On Windows: .venv\Scripts\activate

# Start the MCP Inspector with your server
python -m mcp dev weather_server.py

Cette commande va :

  1. Démarrer votre serveur météo en mode développement
  2. Lancer l'interface web du MCP Inspector
  3. Connecter automatiquement l'Inspector à votre serveur

Vous devriez voir une sortie similaire à :

Starting MCP Inspector...
Server running at: http://localhost:3000
MCP server connected successfully

Utiliser l'interface du MCP Inspector

Ouvrez votre navigateur web et rendez-vous à l'adresse http://localhost:3000. L'interface du MCP Inspector propose plusieurs sections pour tester votre serveur :

Panneau d'informations du serveur : Affiche le nom, la version et le statut de connexion de votre serveur. Vous devriez voir "weather-server" indiqué comme connecté.

Section Outils : Liste tous les outils disponibles avec leurs descriptions et schémas de paramètres. Pour votre serveur météo, vous devriez voir :

  • get_forecast - Obtenir les prévisions météo pour des coordonnées
  • get_alerts - Obtenir les alertes météo actives pour un État américain
  • analyze_weather_trends - Analyse météo assistée par IA (si vous avez implémenté l'Étape 6)

Section Ressources : Affiche les ressources disponibles qui fournissent des informations contextuelles :

  • weather://stations/{state} - Informations sur les stations météo pour les États

Tester vos outils

Testons chaque outil de manière systématique :

Tester l'outil de prévisions :

  1. Cliquez sur l'outil get_forecast dans l'Inspector
  2. Saisissez des coordonnées de test :
    • Latitude : 40.7128 (New York)
    • Longitude : -74.0060
  3. Cliquez sur "Execute Tool"
  4. Vérifiez que vous recevez des prévisions météo correctement formatées avec la température, le vent et des informations de prévision détaillées

Tester l'outil d'alertes :

  1. Cliquez sur l'outil get_alerts
  2. Saisissez un code d'État : CA (Californie)
  3. Cliquez sur "Execute Tool"
  4. Vérifiez que vous recevez soit des alertes actives, soit un message "No active alerts"

Tester la validation des entrées :

  1. Essayez des coordonnées invalides (par exemple, latitude : 100, longitude : 200)
  2. Essayez des codes d'État invalides (par exemple, XYZ ou California)
  3. Vérifiez que votre serveur renvoie des messages d'erreur appropriés

Tester les ressources

Tester la ressource des stations météo :

  1. Rendez-vous dans la section Ressources
  2. Recherchez la ressource weather://stations/{state}
  3. Cliquez dessus et saisissez un code d'État comme TX
  4. Vérifiez que vous recevez une liste de stations météo avec leurs noms, identifiants et altitudes

Surveiller les journaux du serveur

Pendant les tests, gardez un œil sur le terminal où vous avez démarré l'Inspector. Vous devriez voir des messages de journal indiquant :

  • Les requêtes API réussies vers le National Weather Service
  • Tout message d'erreur ou avertissement
  • Les confirmations d'exécution des outils

Exemple de sortie de journal :

INFO: Tool 'get_forecast' called with params: {'latitude': 40.7128, 'longitude': -74.0060}
INFO: API request successful: https://api.weather.gov/points/40.7128,-74.0060
INFO: Forecast data retrieved successfully

Résoudre les problèmes courants

Si vous rencontrez des problèmes pendant les tests :

Le serveur ne démarre pas :

  • Vérifiez que toutes les dépendances sont installées : pip install -r requirements.txt
  • Vérifiez que votre environnement virtuel est activé
  • Recherchez d'éventuelles erreurs de syntaxe dans votre code

Les outils renvoient des erreurs :

  • Vérifiez votre connexion Internet (le serveur doit accéder à weather.gov)
  • Vérifiez que l'API du National Weather Service est accessible
  • Examinez les messages d'erreur dans les journaux du serveur

Aucune donnée renvoyée :

  • Essayez d'autres coordonnées (assurez-vous qu'elles se situent aux États-Unis)
  • Vérifiez que les codes d'État sont des abréviations valides à deux lettres
  • Vérifiez que les réponses de l'API ne sont pas bloquées par des pare-feu

Valider le format de sortie

Assurez-vous que les sorties de vos outils sont correctement formatées :

  • Les prévisions météo doivent être lisibles par un humain
  • Les informations d'alerte doivent inclure tous les détails pertinents
  • Les messages d'erreur doivent être clairs et exploitables
  • Toutes les réponses doivent être des chaînes valides (sérialisables en JSON)

Une fois que vous avez testé en profondeur votre serveur avec le MCP Inspector et confirmé que tous les outils et ressources fonctionnent correctement, vous êtes prêt à l'intégrer à des assistants IA comme Claude Desktop.

Enregistrer votre serveur auprès de Claude Desktop

Maintenant que vous disposez d'un serveur MCP fonctionnel, configurons-le pour qu'il fonctionne avec Claude Desktop. Cela implique de modifier le fichier de configuration de Claude Desktop afin d'y enregistrer votre serveur.

Localiser le fichier de configuration

Le fichier de configuration de Claude Desktop se trouve à différents emplacements selon votre système d'exploitation :

macOS :

~/Library/Application Support/Claude/claude_desktop_config.json

Windows :

%APPDATA%\Claude\claude_desktop_config.json

Linux :

~/.config/Claude/claude_desktop_config.json

Configurer votre serveur

Créez ou modifiez le fichier de configuration pour y inclure votre serveur météo. Voici la configuration de base :

{
  "mcpServers": {
    "weather-server": {
      "command": "python",
      "args": ["weather_server.py"],
      "cwd": "/path/to/your/weather-mcp-server"
    }
  }
}

Remplacez /path/to/your/weather-mcp-server par le chemin réel de votre répertoire de projet.

Configuration alternative utilisant l'environnement virtuel

Si vous souhaitez utiliser explicitement l'interpréteur Python de votre environnement virtuel :

{
  "mcpServers": {
    "weather-server": {
      "command": "/path/to/your/weather-mcp-server/.venv/bin/python",
      "args": ["weather_server.py"],
      "cwd": "/path/to/your/weather-mcp-server"
    }
  }
}

Sous Windows, le chemin serait :

{
  "mcpServers": {
    "weather-server": {
      "command": "C:\\path\\to\\your\\weather-mcp-server\\.venv\\Scripts\\python.exe",
      "args": ["weather_server.py"],
      "cwd": "C:\\path\\to\\your\\weather-mcp-server"
    }
  }
}

Tester l'intégration

  1. Enregistrez le fichier de configuration
  2. Redémarrez complètement Claude Desktop (quittez et rouvrez)
  3. Démarrez une nouvelle conversation
  4. Essayez de demander à Claude d'obtenir des informations météo pour un lieu

Vous devriez voir Claude utiliser vos outils météo pour fournir des informations météorologiques en temps réel !

Résoudre les problèmes d'intégration avec Claude Desktop

Si votre serveur n'apparaît pas dans Claude Desktop :

  1. Vérifiez la syntaxe du fichier de configuration - Utilisez un validateur JSON pour vous assurer d'un formatage correct
  2. Vérifiez les chemins de fichiers - Assurez-vous que tous les chemins de la configuration sont absolus et corrects
  3. Vérifiez les autorisations - Assurez-vous que Claude Desktop peut exécuter votre environnement Python
  4. Examinez les journaux - Claude Desktop peut afficher des messages d'erreur dans son interface
  5. Testez d'abord avec le MCP Inspector - Vérifiez toujours que votre serveur fonctionne avec l'Inspector avant de configurer Claude Desktop

Ajouter des fonctionnalités avancées

Ajoutons maintenant quelques fonctionnalités avancées pour rendre notre serveur plus puissant et prêt pour la production.

Étape 6 : Ajouter le sampling MCP

Le sampling MCP permet à votre serveur de demander des complétions d'IA au client, permettant une analyse intelligente des données météorologiques :

"""
Step 6: Add MCP sampling
"""
import sys
import httpx
from mcp.server.fastmcp import FastMCP, Context
from mcp.server.session import ServerSession

mcp = FastMCP("weather-server")

# Configuration
NWS_API_BASE = "https://api.weather.gov"
USER_AGENT = "weather-mcp-server/1.0"

async def make_nws_request(url: str) -> dict | None:
    """Make a request to the National Weather Service API with proper error handling"""
    headers = {
        "User-Agent": USER_AGENT,
        "Accept": "application/geo+json"
    }

    async with httpx.AsyncClient() as client:
        try:
            response = await client.get(url, headers=headers, timeout=30.0)
            response.raise_for_status()
            return response.json()
        except Exception as e:
            print(f"API request failed: {e}", file=sys.stderr)
            return None

# Previous tools (get_forecast, get_alerts) - same as Step 5
@mcp.tool()
async def get_forecast(latitude: float, longitude: float) -> str:
    """Get weather forecast for a specific location using coordinates"""
    # [Previous implementation from Step 5]
    # ... (keeping this concise for the tutorial)
    pass

@mcp.tool()
async def get_alerts(state: str) -> str:
    """Get active weather alerts for a US state"""
    # [Previous implementation from Step 5]
    # ... (keeping this concise for the tutorial)
    pass

# New AI-powered tool using sampling
@mcp.tool()
async def analyze_weather_trends(
    state: str,
    ctx: Context[ServerSession, None]
) -> str:
    """Analyze weather alert trends for a state using AI-powered analysis"""
    # First, gather current weather alert data
    alerts_data = await get_alerts(state)

    if "No active weather alerts" in alerts_data or "Error:" in alerts_data:
        return f"No weather alerts available for analysis in {state}"

    # Use sampling to analyze the weather data
    analysis_prompt = f"""
    Analyze the following weather alert data for {state} and provide insights about:

    1. The types of weather events currently affecting the region
    2. The severity and geographic distribution of alerts
    3. Potential impacts on daily activities and safety
    4. Any notable patterns or unusual weather conditions

    Weather Alert Data:
    {alerts_data}

    Provide a concise but comprehensive analysis that would be helpful for emergency management and public safety planning.
    """

    try:
        # Request AI analysis through sampling
        response = await ctx.request_sampling(
            messages=[{
                "role": "user",
                "content": {
                    "type": "text",
                    "text": analysis_prompt
                }
            }],
            modelPreferences={
                "intelligencePriority": 0.8,  # High intelligence for analysis
                "speedPriority": 0.4,         # Moderate speed requirement
                "costPriority": 0.3           # Cost is less important for analysis
            },
            systemPrompt="You are a meteorological analyst with expertise in weather pattern analysis and emergency management.",
            maxTokens=500
        )

        return f"Weather Trend Analysis for {state}:\n\n{response.content.text}"

    except Exception as e:
        return f"Unable to generate weather analysis: {str(e)}"

if __name__ == "__main__":
    mcp.run(transport='stdio')

Étape 7 : Ajouter l'authentification pour le déploiement en production

Pour les déploiements en production, vous voudrez ajouter une authentification. Voici comment ajouter une authentification de base par jeton bearer :

"""
Step 7: Add authentication for production deployment
"""
import os
import sys
import httpx
import jwt
from datetime import datetime
from mcp.server.fastmcp import FastMCP, Context
from mcp.server.session import ServerSession

mcp = FastMCP("weather-server")

# Configuration
NWS_API_BASE = "https://api.weather.gov"
USER_AGENT = "weather-mcp-server/1.0"
JWT_SECRET = os.getenv("JWT_SECRET", "your-secret-key")

class AuthenticationError(Exception):
    """Custom exception for authentication failures"""
    pass

def verify_bearer_token(token: str) -> dict:
    """Verify and decode a JWT bearer token"""
    try:
        payload = jwt.decode(token, JWT_SECRET, algorithms=["HS256"])

        # Check token expiration
        if datetime.utcnow().timestamp() > payload.get("exp", 0):
            raise AuthenticationError("Token has expired")

        return payload

    except jwt.InvalidTokenError as e:
        raise AuthenticationError(f"Invalid token: {str(e)}")

def require_authentication(func):
    """Decorator for tools that require authentication"""
    async def wrapper(*args, **kwargs):
        # In a real implementation, you'd extract the auth header from the request context
        # For this tutorial, we'll simulate authentication

        # Check if running in authenticated mode
        if os.getenv("REQUIRE_AUTH", "false").lower() == "true":
            # In production, extract token from request headers
            token = os.getenv("AUTH_TOKEN")
            if not token:
                return "Authentication required: Please provide a valid bearer token"

            try:
                user_context = verify_bearer_token(token)
                kwargs['user_context'] = user_context
            except AuthenticationError as e:
                return f"Authentication failed: {str(e)}"

        return await func(*args, **kwargs)

    return wrapper

# Previous API helper function
async def make_nws_request(url: str) -> dict | None:
    """Make a request to the National Weather Service API with proper error handling"""
    headers = {
        "User-Agent": USER_AGENT,
        "Accept": "application/geo+json"
    }

    async with httpx.AsyncClient() as client:
        try:
            response = await client.get(url, headers=headers, timeout=30.0)
            response.raise_for_status()
            return response.json()
        except Exception as e:
            print(f"API request failed: {e}", file=sys.stderr)
            return None

# Authenticated tools
@mcp.tool()
@require_authentication
async def get_secure_forecast(
    latitude: float,
    longitude: float,
    user_context: dict = None
) -> str:
    """Get weather forecast with authentication and audit logging"""
    user_id = user_context.get("sub", "unknown") if user_context else "anonymous"
    print(f"Forecast request from user {user_id} for {latitude}, {longitude}", file=sys.stderr)

    # Use the same forecast logic as before
    # [Implementation details omitted for brevity]
    return f"Authenticated forecast for {latitude}, {longitude} (User: {user_id})"

if __name__ == "__main__":
    # Determine transport based on environment
    transport = os.getenv("MCP_TRANSPORT", "stdio")

    if transport == "http":
        # Production HTTP deployment with authentication
        port = int(os.getenv("PORT", 8000))
        host = os.getenv("HOST", "0.0.0.0")

        print(f"Starting secure MCP server on {host}:{port}", file=sys.stderr)
        mcp.run(transport="http", host=host, port=port)
    else:
        # Development STDIO deployment
        print("Starting MCP server in STDIO mode", file=sys.stderr)
        mcp.run(transport="stdio")

Considérations pour le déploiement en production

Lorsque vous déployez votre serveur MCP en production, tenez compte de ces facteurs importants :

Configuration de l'environnement

Utilisez des variables d'environnement pour la configuration :

# .env file for production
MCP_TRANSPORT=http
HOST=0.0.0.0
PORT=8000
REQUIRE_AUTH=true
JWT_SECRET=your-production-secret-key
NWS_API_USER_AGENT=your-production-app/1.0

Déploiement avec Docker

Créez un Dockerfile pour un déploiement conteneurisé :

FROM python:3.11-slim

WORKDIR /app

# Copy requirements and install dependencies
COPY requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt

# Copy project files
COPY weather_server.py ./

# Expose port
EXPOSE 8000

# Run the server
CMD ["python", "weather_server.py"]

Construisez et exécutez le conteneur Docker :

# Build the image
docker build -t weather-mcp-server .

# Run the container
docker run -p 8000:8000 -e MCP_TRANSPORT=http weather-mcp-server

Surveillance et journalisation

Mettez en place une journalisation appropriée pour la production :

import logging
import sys

# Configure logging for production
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
    handlers=[
        logging.FileHandler('/var/log/mcp-server.log'),
        logging.StreamHandler(sys.stderr)
    ]
)

logger = logging.getLogger(__name__)

# Use logger throughout your application
logger.info("Server starting up")
logger.error("API request failed", exc_info=True)

Résoudre les problèmes courants

Problèmes de journalisation en STDIO

Le problème le plus courant est l'écriture sur stdout dans les serveurs STDIO :

# Wrong - breaks the protocol
print("Debug message")

# Correct - use stderr
print("Debug message", file=sys.stderr)

# Better - use logging
import logging
logging.basicConfig(stream=sys.stderr)
logger = logging.getLogger(__name__)
logger.info("Debug message")

Erreurs de sérialisation JSON

Assurez-vous que toutes les valeurs de retour des outils sont sérialisables en JSON :

# Wrong - returns complex object
@mcp.tool()
def bad_tool():
    return SomeComplexObject()

# Correct - returns string
@mcp.tool()
def good_tool():
    result = SomeComplexObject()
    return str(result)  # or result.to_dict() if available

Problèmes d'authentification

Pour les serveurs HTTP, vérifiez votre configuration d'authentification :

# Debug authentication setup
def debug_auth():
    required_vars = ["JWT_SECRET", "AUTH_TOKEN"]
    for var in required_vars:
        if not os.getenv(var):
            print(f"Missing environment variable: {var}", file=sys.stderr)

Prochaines étapes et sujets avancés

Maintenant que votre serveur MCP météo est terminé, vous êtes prêt à explorer des schémas plus avancés :

Étendre votre serveur

Envisagez d'ajouter ces fonctionnalités :

  • Données météorologiques historiques provenant d'API supplémentaires
  • Intégration de cartes météo avec des ressources d'images
  • Alertes en temps réel à l'aide de connexions WebSocket
  • Prédictions par apprentissage automatique en utilisant le sampling pour l'analyse

Schémas architecturaux avancés

Explorez ces schémas pour les déploiements complexes :

  • Architectures multi-serveurs avec des domaines spécialisés
  • Composition de serveurs combinant plusieurs serveurs MCP
  • Déploiements distribués sur plusieurs régions cloud
  • Intégration en microservices avec les systèmes existants

Références

[1] Introduction d'Anthropic au Model Context Protocol

[2] Documentation du Model Context Protocol

[3] SDK Python du Model Context Protocol

[4] Guide officiel de création de serveurs MCP

[5] Vue d'ensemble de l'architecture MCP

[6] Documentation sur le sampling MCP

[7] Spécification d'autorisation MCP

Testez votre serveur MCP dans le navigateur

Collez l'URL d'un serveur MCP et voyez tous les outils, ressources et prompts exposés, avec les schémas complets et le journal des requêtes. Gratuit, sans installation ni inscription.

Ouvrir le MCP Inspector