[!NOTE] Traduction automatique Cet article a été traduit automatiquement depuis la version originale en anglais.

MCP Tutoriel serveur avec uv et FastMCP : Création d’un serveur FeatureStoreLite

Ce tutoriel sur le serveur MCP permet de créer un petit serveur de stockage de fonctionnalités à l’aide de FastMCP, de le lancer avec uv, et de l’intégrer à Claude Desktop.

TL;DR : Un petit serveur FastMCP suffit pour exposer une utilité locale utile à un agent. Utilisez uv afin d’assurer une configuration reproductible, restreignez les limites du serveur, validez les entrées à l’aide de schémas typés, et testez la fonction MCP avant de la connecter à un modèle.


Qu’est-ce qu’un serveur MCP ?

Le Model Context Protocol (MCP) est une norme ouverte permettant de relier des assistants AI à des données et des outils externes. Il remplace les intégrations personnalisées distinctes nécessaires pour chaque outil par un ensemble unique de conventions.

Dans ce tutoriel, nous allons mettre en place un serveur FeatureStoreLite MCP. Il se situe entre un LLM et un stockage de caractéristiques (une base de données contenant des caractéristiques ML précalculées), et fournit des outils permettant de consulter ainsi que d’écrire des vecteurs de caractéristiques identifiés par utilisateur, produit ou document.

Pourquoi le développer ?

Vous êtes un ingénieur ML en train de déboguer un pipeline. Au lieu d’accéder directement à SQL ou d’écrire un script rapide pour vérifier les valeurs des caractéristiques, vous interrogez directement Claude en demandant : « Quel est le vecteur de caractéristiques de l’utilisateur_123 ? » ou « Montrez-moi les métadonnées du produit_abc._ C’est le serveur MCP qui rend cela possible.

Pourquoi l’utiliser uv?

Nous utiliserons UV pour installer des paquets, résoudre les dépendances et gérer l’environnement virtuel. Plus tard, uv run --with mcp[cli] ... Cela permettra également à la configuration de Claude Desktop d’indiquer directement sa dépendance. Ainsi, il n’y aura plus d’environnement distinct qui risquerait de devenir désynchronisé.

Aperçu de l’architecture

Les quatre composants et leur interaction :

MCP Architecture

  1. L’utilisateur pose une question en langage naturel.
  2. Claude Desktop est le client MCP. Il sélectionne un outil en fonction de la question et l’appelle.
  3. Notre FastMCP le serveur expose get_feature et store_feature en tant qu’outils MCP.
  4. SQLite sert de stockage de base de données pour les vecteurs de caractéristiques.

2. Configuration et installation

2.1. Installation uv

Si vous ne l’avez pas déjà uv, installez-le. Le reste du tutoriel suppose qu’il est déjà présent sur votre PATH.

# macOS/Linux
curl -LsSf https://astral.sh/uv/install.sh | sh

# Or via Homebrew
brew install uv

2.2. Initialiser le projet

Créez un nouveau répertoire et initialisez un projet Python. uv init crée un pyproject.toml pour vous.

# Create project directory
mkdir mcp-featurestore
cd mcp-featurestore

# Initialize Python project
uv init

# Add the MCP SDK with CLI tools
uv add "mcp[cli]"

3. Construction du serveur

Deux fichiers, séparés par domaine d’application :

  1. database.py gère les opérations SQLite.
  2. featurestore_server.py Définit le serveur MCP.

3.1. La couche de base de données (database.py)

Ce module gère la connexion SQLite ainsi que quelques fonctions d’aide. Il est initialisé avec deux lignes d’exemple afin que le serveur dispose de données à retourner dès la première requête.

Créer database.py:

# database.py
import json
import os
import sqlite3


def get_db_path() -> str:
    """Get the database path - always in the script's directory"""
    script_dir = os.path.dirname(os.path.abspath(__file__))
    return os.path.join(script_dir, "features.db")


def init_db() -> None:
    """Initialize the feature store database with table and sample data"""
    conn = sqlite3.connect(get_db_path())
    conn.execute("""
        CREATE TABLE IF NOT EXISTS features (
            key TEXT PRIMARY KEY,
            vector TEXT NOT NULL,
            metadata TEXT,
            created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
        )
    """)

    # Sample data for experimentation
    example_features = [
        (
            "user_123",
            "[0.1, 0.2, -0.5, 0.8, 0.3, -0.1, 0.9, -0.4]",
            json.dumps({"type": "user", "id": 123, "segment": "premium"}),
        ),
        (
            "product_abc",
            "[0.7, -0.3, 0.4, 0.1, -0.8, 0.6, 0.2, -0.5]",
            json.dumps({"type": "product", "id": "abc", "category": "electronics"}),
        ),
    ]

    # Insert if not exists
    for key, vector, metadata in example_features:
        try:
            conn.execute(
                "INSERT INTO features (key, vector, metadata) VALUES (?, ?, ?)",
                (key, vector, metadata),
            )
        except sqlite3.IntegrityError:
            pass  # Already exists

    conn.commit()
    conn.close()


def get_db_connection() -> sqlite3.Connection:
    """Get a database connection"""
    return sqlite3.connect(get_db_path())


if __name__ == "__main__":
    init_db()
    print("✅ Database initialized successfully!")

Initialiser la base de données :

uv run python database.py

3.2. Le serveur MCP (featurestore_server.py)

FastMCP Il effectue la majeure partie du travail. En décorant une fonction Python ordinaire, celle‑ci est enregistrée en tant qu’outil ou ressource MCP. La documentation devient la description affichée par LLM ; il convient donc de la rédiger pour un LLM, et non uniquement pour les humains.

Créer featurestore_server.py:

# featurestore_server.py
import json
from mcp.server.fastmcp import FastMCP
from database import get_db_connection, init_db

# Initialize the MCP Server
mcp = FastMCP("FeatureStoreLite")

# Ensure DB is ready when server starts
init_db()


@mcp.resource("schema://main")
def get_schema() -> str:
    """
    Resource: Provide the database schema.
    Resources are passive data that LLMs can read like files.
    """
    conn = get_db_connection()
    try:
        schema = conn.execute(
            "SELECT sql FROM sqlite_master WHERE type='table'"
        ).fetchall()
        return "\n".join(sql[0] for sql in schema if sql[0]) or "No tables found."
    finally:
        conn.close()


@mcp.tool()
def store_feature(key: str, vector: str, metadata: str | None = None) -> str:
    """
    Tool: Store a feature vector.
    Tools are executable functions that LLMs can call to perform actions.
    """
    conn = get_db_connection()
    try:
        # Validate that vector is valid JSON
        json.loads(vector)

        conn.execute(
            "INSERT OR REPLACE INTO features (key, vector, metadata) VALUES (?, ?, ?)",
            (key, vector, metadata),
        )
        conn.commit()
        return f"Successfully stored feature '{key}'"
    except json.JSONDecodeError:
        return "Error: Vector must be a valid JSON array string (e.g., '[0.1, 0.2]')"
    except Exception as e:
        return f"Error: {str(e)}"
    finally:
        conn.close()


@mcp.tool()
def get_feature(key: str) -> str:
    """
    Tool: Retrieve a feature vector by key.
    """
    conn = get_db_connection()
    try:
        row = conn.execute(
            "SELECT vector, metadata FROM features WHERE key = ?", (key,)
        ).fetchone()

        if row:
            return json.dumps(
                {
                    "key": key,
                    "vector": json.loads(row[0]),
                    "metadata": json.loads(row[1]) if row[1] else None,
                },
                indent=2,
            )
        return f"Feature '{key}' not found."
    finally:
        conn.close()


@mcp.tool()
def list_features() -> str:
    """
    Tool: List all available feature keys.
    """
    conn = get_db_connection()
    try:
        rows = conn.execute("SELECT key FROM features").fetchall()
        return json.dumps([row[0] for row in rows])
    finally:
        conn.close()


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

4. Tests avec l’inspecteur MCP

Avant de le connecter à Claude, effectuez une vérification de base du serveur à l’aide de l’MCP Inspector. Il s’agit d’une petite interface web permettant d’appeler des outils et de lire des ressources directement.

uv run mcp dev featurestore_server.py

La commande lance le serveur et ouvre l’Inspecteur dans votre navigateur (généralement à http://localhost:5173).

Inspecteur{width=“600”}

Appel get_feature avec key="user_123". Si le serveur renvoie JSON pour la ligne de graine, il fonctionne correctement.


5. Connexion à Claude Desktop

Une fois que l’Inspecteur confirme que le serveur fonctionne correctement, enregistrez‑le dans Claude Desktop.

5.1. Configurer Claude

Modifiez le fichier de configuration de Claude Desktop :

Ajoutez votre serveur à la mcpServers objet :

{
    "mcpServers": {
        "featurestore": {
            "command": "uv",
            "args": [
                "run",
                "--with",
                "mcp[cli]",
                "mcp",
                "run",
                "/ABSOLUTE/PATH/TO/mcp-featurestore/featurestore_server.py"
            ]
        }
    }
}

Important : le chemin vers featurestore_server.py Il doit s’agir d’un chemin absolu. Un chemin relatif échouera de manière silencieuse, car Claude Desktop s’exécute depuis un répertoire de travail différent.

5.2. Fonctionnement de l’interaction

Voici ce qui s’exécute du début à la fin lorsque Claude a besoin de rechercher une fonctionnalité :

MCP Flux de travail

  1. Claude identifie les outils disponibles (get_feature, list_features, et ainsi de suite).
  2. Il détermine que la question nécessite des données provenant du feature store.
  3. Il crée un tool call et l’envoie à votre serveur.
  4. Le serveur exécute la fonction Python et renvoie le résultat.
  5. Claude utilise ce résultat pour rédiger la réponse finale.

5.3. Exemples de requêtes

Redémarrez Claude Desktop et essayez quelques prompts :

  1. « Énumérez toutes les fonctionnalités disponibles. » Question 2{width=“600”}

  2. « Obtenir le vecteur de caractéristiques pour user_123. » Question 3{width=“600”}

  3. « Stocker une nouvelle caractéristique pour ‘new_item’ avec le vecteur [0.5, 0.5] et les métadonnées {‘type’: ‘test’}. »


6. Résolution des problèmes

Quelques modes de défaillance à connaître :


7. Conclusion

C’est tout : un FastMCP un serveur, un stockage de secours SQLite, ainsi qu’une configuration de Claude Desktop qui fait référence à un uv run commande. La même logique s’applique à tout ce qui peut être encapsulé dans une fonction Python. Il suffit de remplacer les appels à SQLite par un véritable entrepôt de fonctionnalités, un API interne, ou un registre de modèles, afin que le serveur reste compact.

Points clés

  1. MCP représente une frontière de outil, et non une raison de rendre accessibles toutes les fonctions internes.
  2. Gardez les outils serveur de taille réduite, typés, et faciles à tester, sans avoir recours à un LLM.
  3. Utilisez uv afin que l’environnement pédagogique puisse être reconstruit à partir de zéro.
  4. Considérez le serveur MCP comme du code en production dès qu’un agent peut y accéder.

Références