[!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í:
- El usuario plantea una pregunta en lenguaje natural.
- Claude Desktop es el cliente MCP. Este selecciona una herramienta en función de la pregunta y la activa.
- Nuestro
FastMCPEl servidor exponeget_featureystore_featurecomo herramientas MCP. - 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:
database.pyManeja operaciones con SQLite.featurestore_server.pyDefine 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).
{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:
- macOS:
~/Library/Application Support/Claude/claude_desktop_config.json - Windows:
%APPDATA%/Claude/claude_desktop_config.json
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.pyDebe 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:
- Claude identifica las herramientas disponibles (
get_feature,list_features, y así sucesivamente). - Determina que la pregunta requiere datos del almacén de características.
- Crea un tool call y lo envía a su servidor.
- El servidor ejecuta la función en Python y devuelve el resultado.
- Claude utiliza ese resultado para redactar la respuesta final.
5.3. Ejemplos de consultas
Reinicie Claude Desktop e intente realizar algunos prompts:
-
“Enumere todas las funcionalidades disponibles.”
{width=“600”} -
“Obtener el vector de características para user_123.”
{width=“600”} -
“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:
-
“Error en la conexión al servidor”:
- Verifique los registros en
~/Library/Logs/Claude/mcp.logen macOS. - Asegúrese de que la configuración utilice una ruta absoluta y no una relativa.
- Asegúrese
uvestá en la versión de Claude DesktopPATH. Si no es así, indique la ruta binaria completa.which uv(le indicará dónde se encuentra).
- Verifique los registros en
-
“Error de ejecución de la herramienta”:
- Reproducídolo en el Inspector con
uv run mcp dev featurestore_server.py. El inspector muestra el error bruto, que normalmente Claude Desktop ignora. - Verifique que
features.dbse está creando junto adatabase.py. Si el directorio de trabajo cambia, la ruta relativa al script enget_db_path()Eso es lo que realmente te salva.
- Reproducídolo en el Inspector con
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
- MCP representa un límite de herramientas, y no una justificación para exponer todas las funciones internas.
- Mantén las herramientas del servidor pequeñas, tipadas y fáciles de probar, sin necesidad de utilizar un LLM.
- Utiliza uv para que el entorno de tutoriales pueda reconstruirse desde cero.
- Considera al servidor MCP como código de producción una vez que un agente pueda llamarlo.