iCards MCP 🎴
Servidor MCP (Model Context Protocol) para gestionar flashcards, construido con FastMCP Python.
¿Qué es MCP?
El Model Context Protocol (MCP) permite que los LLMs (Large Language Models) interactúen de forma estandarizada con herramientas y datos externos. Es como un "puerto USB-C para IA":
- Tools: Funciones que el LLM puede ejecutar (como
add_flashcard,list_decks) - Resources: Datos que el LLM puede leer (documentación, contenido de decks)
- Prompts: Templates reutilizables para interacciones comunes
Este servidor expone las capacidades de iCards a través de MCP, permitiendo que asistentes de IA gestionen tus flashcards de forma inteligente.
✨ Features
- 🚀 FastMCP 2.0: Framework moderno y Pythonic para MCP
- 🎴 Gestión de Flashcards: Tools para crear, editar y gestionar flashcards
- 🌐 Comunicación HTTP: Se conecta a la API REST de iCards
- 📚 Instrucciones Centralizadas: Carga documentación desde ubicación externa compartida
- ⚙️ Configuración por entornos: Local y Producción
- 📦 Estructura modular: Servicios, configuración y extensibilidad
- 🔒 Secure by design: Sin acceso directo a BD, solo via API
📖 Instrucciones Externas
Las instrucciones del MCP se cargan desde una ubicación externa compartida: Path: /Users/esanz/Desktop/ia-mvp/project/server/InstructionsMCP/api_instructions.md
Beneficios:
- ✅ Una sola fuente de verdad
- ✅ Sincronización automática entre proyectos
- ✅ Mantenimiento centralizado de documentación
🚀 Quickstart
1. Instalar dependencias
Este proyecto usa uv para gestionar dependencias (recomendado por FastMCP).
# Instalar uv si no lo tienes
curl -LsSf https://astral.sh/uv/install.sh | sh
# Instalar dependencias del proyecto
uv syncAlternativamente, puedes usar pip:
pip install -r requirements.txt2. Verificar instalación
uv run fastmcp versionDeberías ver:
FastMCP version: 2.11.3
MCP version: 1.20.0
Python version: 3.13.3
Platform: macOS-15.7.1-arm64-arm-64bit3. Configurar el entorno
Para que el MCP funcione correctamente, necesitas configurar el token de autenticación:
# Copiar el archivo de ejemplo
cp env.example .env.local
# Obtener el JWT token (requerido)
curl -X POST http://localhost:3000/api/auth/login \
-H "Content-Type: application/json" \
-d '{"username": "tu-usuario", "password": "tu-password"}'
# Copiar el valor del campo "token" y agregarlo a .env.local
echo "AUTH_TOKEN=tu_jwt_token_aqui" >> .env.local
# Opcionalmente configurar otros valores
# API_BASE_URL=http://tu-api-url:puerto
# API_TIMEOUT=304. Ejecutar el servidor
# Con uv (recomendado)
uv run python server.py
# O con Python directamente si instalaste con pip
python server.pyEl servidor MCP estará disponible vía stdio/SSE según tu configuración.
🔍 Validación automática al inicio
El servidor realiza validación automática al iniciarse:
- Health Check: Verifica que la API esté funcionando
- Token Validation: Intenta obtener datos del usuario para validar el JWT
- Error Handling: Si falla, muestra mensajes claros y se detiene
Ejemplo de output exitoso:
🚀 Starting iCards MCP Server...
🔍 Validating API connection...
🏥 Checking API health at http://localhost:3000/api/health...
✅ API health check passed: {'ok': True}
🔐 Validating token by fetching decks...
✅ Token validation passed - found 5 decks
🎉 API connection and token validation successful!
🎯 Starting MCP server and waiting for requests...5. Probar el servidor
Ejecuta el script de prueba incluido:
uv run python test_server.pyO prueba desde Python:
import asyncio
from fastmcp import Client
from fastmcp.client.transports import StdioTransport
async def main():
transport = StdioTransport("python", ["server.py"])
async with Client(transport) as client:
# Listar decks
result = await client.call_tool(
name="list_decks",
arguments={}
)
print(result.data)
asyncio.run(main())📁 Estructura del Proyecto
iCardsMCP/
├── server.py # Punto de entrada del servidor MCP
├── app/
│ ├── config/
│ │ └── config.py # Configuración por entornos (local, prod)
│ ├── services/ # Lógica de negocio (próximamente)
│ │ ├── flashcard_service.py
│ │ ├── deck_service.py
│ │ └── study_service.py
│ └── adapters/ # Adaptadores HTTP (próximamente)
│ └── icards_api_adapter.py
├── requirements.txt # Dependencias del proyecto
├── env.example # Ejemplo de configuración
└── README.md # Este archivo🛠️ Tools Disponibles
📝 add_flashcard
Agrega una nueva flashcard a un deck. Resuelve automáticamente el deck_name a deckId y el tag_name (opcional) a tagId.
Parámetros:
front(requerido): Pregunta o frente de la tarjetaback(requerido): Respuesta o reverso de la tarjetadeck_name(requerido): Nombre del deck (se resuelve a deckId automáticamente)difficulty_level(opcional): Dificultad 1-3 (default: 2)tag_name(opcional): Nombre de un tag existente en el deck
{
"front": "¿Qué es MCP?",
"back": "Model Context Protocol - Un protocolo para conectar LLMs a herramientas",
"deck_name": "MCP Basics",
"difficulty_level": 2,
"tag_name": "Conceptos" # Opcional
}Nota: El backend API solo soporta un tag por flashcard. Si necesitas múltiples tags, deberás usar la API directamente.
📚 list_decks
Lista todos los decks de flashcards disponibles.
{} # Sin argumentosℹ️ get_deck_info
Obtiene información completa sobre un deck específico, incluyendo:
- Información básica del deck (nombre, descripción, fechas)
- Estadísticas de tarjetas (conteo total, distribución de dificultad)
- Tags del deck con conteo de flashcards por tag
- Progreso de estudio y actividad
Esta herramienta hace múltiples llamadas a la API para consolidar toda la información en una sola respuesta.
{
"deck_name": "Japanese Vocabulary"
}Respuesta incluye:
deck: Información básica del decktags: Lista de tags conflashcard_countpor cada tagtag_count: Total de tags en el deckstatistics: Estadísticas consolidadas (total_flashcards, total_tags, difficulty_distribution, average_difficulty)
🏷️ create_flashcard_template
Crea una plantilla de flashcard basada en el tipo de deck.
{
"deck_type": "vocabulary"
}📋 list_flashcards
Lista las flashcards de un deck específico.
Comportamiento:
- Por defecto: Retorna 50 tarjetas (límite configurable 1-100)
- Con
all_cards=True: Retorna TODAS las tarjetas del deck (sin límite)
Cuándo usar all_cards=True:
- Para análisis completos (contar tags únicos, estadísticas globales)
- Para exportar todas las tarjetas
- Para operaciones que requieren ver el deck completo
Cuándo usar el límite por defecto:
- Para previsualizar tarjetas
- Para navegación paginada
- Para mostrar ejemplos
# Ejemplo 1: Primeras 50 tarjetas (por defecto)
{
"deck_name": "Japanese Vocabulary",
"limit": 50,
"sort_by": "created"
}
# Ejemplo 2: TODAS las tarjetas (para análisis completo)
{
"deck_name": "Japanese Vocabulary",
"all_cards": True
}Nota: Para solo obtener el conteo sin datos, usa count_flashcards que es más eficiente.
🔢 count_flashcards
Cuenta el número total de flashcards en un deck con una sola llamada a la API usando el parámetro all=true. Obtiene el conteo exacto sin límites de paginación.
{
"deck_name": "Japanese Vocabulary"
}⚙️ Configuración
El proyecto usa configuración basada en SCOPE (entornos):
Local (default)
# No requiere configuración
python server.py{
"MCP_ICARDS_NAME": "iCards-MCP-Local",
"API_BASE_URL": "http://localhost:3000",
"LOG_LEVEL": "DEBUG"
}Production
# Requiere variables de entorno
SCOPE=prod API_BASE_URL=https://api.icards.com python server.py{
"MCP_ICARDS_NAME": "iCards-MCP-Prod",
"API_BASE_URL": os.getenv("API_BASE_URL"), # Requerido
"LOG_LEVEL": "WARNING"
}🔧 Agregar Nuevos Tools
- Crear el servicio en
app/services/:
# app/services/study_service.py
from app.config.config import Config
import httpx
async def start_study_session(deck_id: int, card_count: int) -> dict:
"""Inicia una sesión de estudio."""
api_url = Config.get("API_BASE_URL")
async with httpx.AsyncClient() as client:
response = await client.post(
f"{api_url}/api/study/start",
json={"deck_id": deck_id, "card_count": card_count}
)
return response.json()- Registrar el tool en
server.py:
from app.services.study_service import start_study_session
@mcp.tool()
async def start_study(deck_id: int, card_count: int = 10) -> dict:
"""Start a study session with flashcards from a deck."""
return await start_study_session(deck_id, card_count)- Reiniciar el servidor y el tool estará disponible.
📖 Usando con LLMs
Claude Desktop
- Ubicación del archivo de configuración:
- macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
- Configuración completa:
{
"mcpServers": {
"icards": {
"command": "/Users/esanz/Desktop/ia-mvp/iCardsMCP/run_mcp_stdio.sh",
"args": [],
"env": {
"SCOPE": "local",
"API_BASE_URL": "http://localhost:3000",
"API_TIMEOUT": "30",
"AUTH_TOKEN": "tu_jwt_token_aqui"
}
}
},
"isUsingBuiltInNodeForMcp": true
}- Obtener el AUTH_TOKEN:
curl -X POST http://localhost:3000/api/auth/login \
-H "Content-Type: application/json" \
-d '{"username": "tu-usuario", "password": "tu-password"}'- Reiniciar Claude Desktop después de actualizar la configuración.
Cursor / VS Code
Usa el cliente MCP en tu código:
from fastmcp import Client
from fastmcp.client.transports import StdioTransport
transport = StdioTransport("python", ["server.py"])
async with Client(transport) as client:
tools = await client.list_tools()
result = await client.call_tool("add_flashcard", {
"front": "Question",
"back": "Answer",
"deck_name": "My Deck"
})
print(result.data)🧪 Testing
El proyecto incluye dependencias de desarrollo para testing. Para ejecutar tests:
# Instalar dependencias de desarrollo
uv sync --all-extras
# Ejecutar tests (cuando estén implementados)
uv run pytest tests/
# Con coverage
uv run pytest --cov=app tests/
# Ver reporte de coverage en HTML
uv run pytest --cov=app --cov-report=html tests/📚 Recursos
- Documentación FastMCP
- Instalación con uv
- Especificación MCP
- Repositorio FastMCP
- Documentación de uv
- Proyecto iCards principal
🎯 Roadmap
- [ ] Implementar adaptadores HTTP para la API de iCards (Flashcard, Deck, Tag APIs)
- [ ] Agregar más tools (editar flashcards, eliminar decks, gestión de tags)
- [ ] Implementar Resources para exponer contenido de decks
- [ ] Agregar Prompts comunes (generar flashcards basadas en templates)
- [ ] Tests unitarios y de integración
- [ ] Autenticación y autorización
- [ ] Métricas y logging avanzado
- [ ] Deploy a producción
🤝 Contribuir
- Fork el proyecto
- Crea una rama para tu feature (
git checkout -b feature/nueva-funcionalidad) - Commit tus cambios (
git commit -m 'Agrega nueva funcionalidad') - Push a la rama (
git push origin feature/nueva-funcionalidad) - Abre un Pull Request
📄 Licencia
MIT
