[!NOTE] Automatische Übersetzung Dieser Artikel wurde automatisch aus der englischen Originalversion übersetzt.

MCP Server-Tutorial mit uv und FastMCP: Erstellung eines FeatureStoreLite-Servers

Dieses Tutorial zum MCP-Server erstellt einen kleinen Feature-Store-Server mithilfe von FastMCP, startet ihn mit uv und integriert ihn in Claude Desktop.

TL;DR: Ein kleiner FastMCP-Server reicht aus, um ein nützliches lokales Tool für einen Agent bereitzustellen. Verwenden Sie uv für eine wiederholbare Einrichtung, halten Sie die Grenzen des Servers eng, überprüfen Sie die Eingaben mithilfe typisierter Schemata und testen Sie das MCP-Tool vor dem Anschluss an einen Model.


Was ist ein MCP-Server?

Das Model Context Protocol (MCP) ist ein offener Standard zur Verbindung von AI Assistenten mit externen Datenquellen und Tools. Er ersetzt eine individuelle, maßgeschneiderte Integration für jedes Tool durch ein einheitliches Konventionswerkzeug.

In diesem Tutorial werden wir einen Server von FeatureStoreLite MCP erstellen. Er befindet sich zwischen einem LLM und einem Feature-Store – also einer Datenbank mit vorberechneten ML Features – und stellt Werkzeuge zur Abfrage sowie zum Schreiben von Feature-Vektoren bereit, die nach Benutzer, Produkt oder Dokument geordnet sind.

Warum wird das gebaut?

Sie sind ein ML-Entwickler, der einen Pipeline debuggt. Anstatt direkt in SQL einzudringen oder einen schnellen Script zu schreiben, um die Werte der Features zu überprüfen, fragen Sie Claude direkt: „Wie sieht der Feature-Vektor für user_123 aus?“ oder „Zeigen Sie mir die Metadaten für product_abc.“ Der MCP-Server macht das möglich.

Warum verwenden uv?

Wir werden es verwenden. UV um Pakete zu installieren, Abhängigkeiten zu lösen und die virtuelle Umgebung zu verwalten. Später, uv run --with mcp[cli] ... Es ermöglicht zudem, dass die Konfiguration von Claude Desktop ihre Abhängigkeit direkt deklariert. Dadurch entfällt eine separate Umgebung, die aus dem Gleichgewicht geraten könnte.

Übersicht zur Architektur

Die vier Komponenten und ihre gegenseitige Verknüpfung:

MCP Architektur

  1. Der Benutzer stellt eine Frage in natürlicher Sprache.
  2. Claude Desktop ist der MCP-Client. Er wählt auf Basis der Frage ein passendes Tool aus und ruft dieses auf.
  3. Unsere FastMCP der Server stellt bereit get_feature und store_feature als MCP-Tools.
  4. SQLite dient als zugrundeliegende Datenbank für die Feature-Vektoren.

2. Einrichtung und Installation

2.1. Installation uv

Wenn Sie noch keinen solchen Agenten besitzen uvInstallieren Sie es. Der Rest des Tutorials geht davon aus, dass es auf Ihrem System vorhanden ist. PATH.

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

# Or via Homebrew
brew install uv

2.2. Projekt initialisieren

Erstellen Sie einen neuen Verzeichnis und initialisieren Sie ein Python-Projekt. uv init erstellt einen pyproject.toml für Sie.

# 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. Aufbau des Servers

Zwei Dateien, nach Aufgabenbereich getrennt:

  1. database.py verarbeitet SQLite-Operationen.
  2. featurestore_server.py definiert den MCP-Server.

3.1. Die Datenbankschicht (database.py)

Dieses Modul verwaltet die SQLite-Verbindung sowie einige Hilfsfunktionen. Es wird mit zwei Beispieldatenzeilen initialisiert, damit der Server bei der ersten Abfrage etwas zurückgeben kann.

erstellen 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!")

Datenbank initialisieren:

uv run python database.py

3.2. Der MCP-Server (featurestore_server.py)

FastMCP erledigt den größten Teil der Arbeit. Wenn man eine einfache Python-Funktion annotiert, wird sie als MCP-Tool oder -Ressource registriert. Die Dokumentation dient dabei als Beschreibung, die vom LLM abgerufen wird; daher sollte sie für LLM verfasst werden und nicht nur für Menschen.

erstellen 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. Testing mit dem MCP Inspector

Bevor Sie dies in Claude anschließen, überprüfen Sie den Server mit dem MCP Inspector auf Funktionalität. Es handelt sich dabei um eine kleine Web-Oberfläche, über die Tools aufgerufen sowie Ressourcen direkt abgerufen werden können.

uv run mcp dev featurestore_server.py

Der Befehl startet den Server und öffnet den Inspector in Ihrem Browser (in der Regel unter http://localhost:5173).

Inspektor{width=“600”}

aufrufen get_feature mit key="user_123". Wenn für die Seed-Zeile der JSON zurückgegeben wird, funktioniert der Server einwandfrei.


5. Anbindung an Claude Desktop

Sobald der Inspektor bestätigt hat, dass der Server funktioniert, registrieren Sie ihn in Claude Desktop.

5.1. Claude konfigurieren

Bearbeiten Sie die Konfigurationsdatei von Claude Desktop:

Fügen Sie Ihren Server hinzu zu mcpServers Objekt:

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

Wichtig: Der Pfad zu featurestore_server.py Es muss ein absoluter Pfad sein. Ein relativer Pfad führt zu einem stillen Fehler, da Claude Desktop aus einem anderen Arbeitsverzeichnis ausgeführt wird.

5.2. Wie die Interaktion funktioniert

Hier ist der Ablauf vom Anfang bis zum Ende, wenn Claude nach einer Funktionsbeschreibung suchen muss:

MCP Workflow

  1. Claude erkennt die verfügbaren Tools.get_feature, list_features, und so weiter).
  2. Es entscheidet, dass für die Beantwortung der Frage Daten aus dem Feature Store benötigt werden.
  3. Es erstellt einen Tool Call und sendet diesen an Ihren Server.
  4. Der Server führt die Python-Funktion aus und gibt das Ergebnis zurück.
  5. Claude verwendet dieses Ergebnis, um die endgültige Antwort zu formulieren.

5.3. Beispielfragen

Starten Sie Claude Desktop erneut und testen Sie einige Prompts:

  1. „Listen Sie alle verfügbaren Funktionen auf.“ Frage 2{width=“600”}

  2. „Erstellen Sie den Eigenschaftsvektor für user_123.“ Frage 3{width=“600”}

  3. „Speichern Sie eine neue Eigenschaft für ‚new_item‘ mit dem Vektor [0.5, 0.5] sowie den Metadaten {‘type’: ‘test’}.“


6. Fehlerbehebung

Einige Ausfallmuster, die man kennen sollte:


7. Fazit

Das ist es also: ein FastMCP ein Server, ein SQLite-Backend-Speicher sowie eine Claude Desktop-Konfiguration, die auf einen … verweist uv run Befehl. Die gleiche Struktur gilt für alles, was in eine Python-Funktion eingebettet werden kann. Ersetzen Sie die SQLite-Aufrufe durch einen echten Feature-Store, ein internes API oder ein Model-Registry – dadurch bleibt der Server weiterhin kompakt.

Wichtige Erkenntnisse

  1. MCP stellt eine Abgrenzung zwischen Tools dar und nicht einen Grund, jede interne Funktion offenzulegen.
  2. Halten Sie Server-Tools klein, typisiert und leicht testbar – ohne die Verwendung eines LLM.
  3. Nutzen Sie uv, damit die Tutorial-Umgebung von Grund auf neu erstellt werden kann.
  4. Behandeln Sie den MCP-Server als Produktionscode, sobald ein Agent darauf zugreifen kann.

Referenzen