[!NOTE] Traducción automática Este artículo se tradujo automáticamente a partir de la versión original en inglés.

MCP Tutorial de servidor con uv y FastMCP: Construir un servidor FeatureStoreLite

Este tutorial sobre servidores MCP permite crear un pequeño servidor de almacén de características utilizando FastMCP, ejecutarlo con uv, e integrarlo en Claude Desktop.

TL;DR: Un servidor FastMCP de pequeñas dimensiones es suficiente para exponer una herramienta local útil a un agente. Utilice uv para garantizar una configuración reproducible, mantenga los límites del servidor lo más estrechos posible, valide las entradas mediante esquemas tipados y pruebe la herramienta MCP antes de conectarla a un modelo.


¿Qué es un servidor MCP?

El Model Context Protocol (MCP) es un estándar abierto destinado a conectar asistentes AI con datos y herramientas externas. Permite sustituir la necesidad de implementar integraciones personalizadas separadas para cada herramienta por un único conjunto de convenciones comunes.

En esta guía vamos a crear un servidor FeatureStoreLite MCP. Este se sitúa entre un LLM y un almacén de características (una base de datos con características ML precalculadas), y ofrece herramientas para consultar y escribir vectores de características indexados por usuario, producto o documento.

¿Por qué desarrollar esto?

Eres un ingeniero de ML que está depurando un pipeline. En lugar de acceder directamente a SQL o de escribir un script rápido para consultar los valores de las características, le preguntas directamente a Claude: “¿Cuál es el vector de características de user_123?” o “Muéstrame la metadata del producto product_abc.” Es el servidor de MCP el que hace posible esto.

¿Por qué utilizarlo? uv?

Utilizaremos UV para instalar paquetes, resolver dependencias y gestionar el entorno virtual. Más adelante, uv run --with mcp[cli] ...

Visión general de la arquitectura

Los cuatro componentes y cómo se integran entre sí:

MCP Arquitectura

  1. El usuario plantea una pregunta en lenguaje natural.
  2. Claude Desktop es el cliente MCP. Este selecciona una herramienta en función de la pregunta y la activa.
  3. Nuestro FastMCP El servidor expone get_feature y store_feature como herramientas MCP.
  4. SQLite es el almacén subyacente para los vectores de características.

2. Configuración e instalación

2.1. Instalar uv

Si aún no dispones de uvInstálalo. El resto del tutorial da por sentado que ya está en tu sistema. PATH.

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

# Or via Homebrew
brew install uv

2.2. Inicializar el proyecto

Crea un directorio nuevo e inicializa un proyecto en Python. uv init crea un pyproject.toml para ti.

# 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. Construcción del servidor

Dos archivos, separados por área de responsabilidad:

  1. database.py Maneja operaciones con SQLite. featurestore_server.py Define el servidor MCP.

3.1. La capa de base de datos (database.py)

Este módulo gestiona la conexión a SQLite y cuenta con algunos auxiliares. Se inicializa con dos filas de ejemplo para que el servidor tenga algo que devolver en la primera consulta.

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

Inicializar la base de datos:

uv run python database.py

3.2. El servidor MCP (featurestore_server.py)

FastMCP Realiza la mayor parte del trabajo. Al decorar una función Python sencilla, esta se registra como una herramienta o recurso MCP. La documentación se convierte en la descripción que visualiza el LLM, por lo que debe redactarse dirigida a un LLM, y no únicamente para humanos.

Crear 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. Pruebas con el inspector MCP

Antes de conectarlo a Claude, realice una comprobación preliminar del servidor mediante el MCP Inspector. Se trata de una sencilla interfaz web para llamar a herramientas y leer recursos directamente.

uv run mcp dev featurestore_server.py

El comando inicia el servidor y abre el Inspector en su navegador (generalmente en http://localhost:5173).

Inspector{width=“600”}

Llamada get_feature con key="user_123". Si devuelve el JSON para la fila de semilla, el servidor está funcionando correctamente.


5. Conexión a Claude Desktop

Una vez que el Inspector confirme que el servidor funciona, regístrelo en Claude Desktop.

5.1. Configurar Claude

Edite el archivo de configuración de Claude Desktop:

Agregue su servidor a la mcpServers objeto:

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

Importante: la ruta hacia featurestore_server.py Debe ser absoluto. Una ruta relativa fallará de forma silenciosa, ya que Claude Desktop se ejecuta desde un directorio de trabajo diferente.

5.2. Cómo funciona la interacción

Esto es lo que se ejecuta de punta a punta cuando Claude necesita realizar una búsqueda de funcionalidades:

MCP Flujo de trabajo

  1. Claude identifica las herramientas disponibles (get_feature, list_features, y así sucesivamente).
  2. Determina que la pregunta requiere datos del almacén de características.
  3. Crea un tool call y lo envía a su servidor.
  4. El servidor ejecuta la función en Python y devuelve el resultado.
  5. Claude utiliza ese resultado para redactar la respuesta final.

5.3. Ejemplos de consultas

Reinicie Claude Desktop e intente realizar algunos prompts:

  1. “Enumere todas las funcionalidades disponibles.” Pregunta 2{width=“600”}

  2. “Obtener el vector de características para user_123.” Pregunta 3{width=“600”}

  3. “Almacenar una nueva característica para ‘new_item’ con el vector [0.5, 0.5] y los metadatos {‘type’: ‘test’}.”


6. Solución de problemas

Algunos modos de fallo que merecen ser conocidos:


7. Conclusión

Eso es todo: un FastMCP servidor, un almacén de respaldo basado en SQLite, y una configuración de Claude Desktop que apunta a uno uv run command. La misma estructura es aplicable a cualquier elemento que se pueda encapsular dentro de una función en Python. Se pueden reemplazar las llamadas a SQLite por un almacén de características real, un API interno, o un registro de modelos, manteniendo así un servidor de tamaño reducido.

Conclusiones clave

  1. MCP representa un límite de herramientas, y no una justificación para exponer todas las funciones internas.
  2. Mantén las herramientas del servidor pequeñas, tipadas y fáciles de probar, sin necesidad de utilizar un LLM.
  3. Utiliza uv para que el entorno de tutoriales pueda reconstruirse desde cero.
  4. Considera al servidor MCP como código de producción una vez que un agente pueda llamarlo.

Referencias