#!/bin/bash # setup-jupyter.sh - Automatiza la configuración de Jupyter + MCP para Claude # Uso: ./setup-jupyter.sh [ruta-proyecto] # Generado para el skill /init-jupyter set -e # Colores para output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' # No Color # ═══════════════════════════════════════════════════════════════ # FUNCIONES AUXILIARES # ═══════════════════════════════════════════════════════════════ log_info() { echo -e "${BLUE}[INFO]${NC} $1"; } log_ok() { echo -e "${GREEN}[OK]${NC} $1"; } log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; } log_error() { echo -e "${RED}[ERROR]${NC} $1"; } find_free_port() { for port in 8888 8889 8890 8891 8892 8893 8894 8895 8896 8897 8898 8899; do if ! lsof -i:$port >/dev/null 2>&1; then echo $port return fi done echo 8888 } # ═══════════════════════════════════════════════════════════════ # PASO 0: CAMBIAR AL DIRECTORIO DEL PROYECTO # ═══════════════════════════════════════════════════════════════ RUTA="${1:-.}" cd "$RUTA" || { log_error "No se pudo acceder a: $RUTA"; exit 1; } PROJECT_PATH=$(pwd) echo "" echo "═══════════════════════════════════════════════════════════════" echo " SETUP JUPYTER + MCP para Claude" echo " Proyecto: $PROJECT_PATH" echo "═══════════════════════════════════════════════════════════════" echo "" # ═══════════════════════════════════════════════════════════════ # PASO 1: VERIFICACIONES DE ESTADO # ═══════════════════════════════════════════════════════════════ log_info "Verificando estado actual..." # Variables de estado MCP_LOCAL=false REPO_CONFIG=false JUPYTER_RUNNING=false RULES_OK=false GLOBAL_MCP=false CURRENT_PORT="" # 1. MCP instalado localmente if [ -f .venv/bin/jupyter-mcp-server ]; then MCP_LOCAL=true log_ok "MCP instalado en .venv/bin/" else log_warn "MCP no instalado localmente" fi # 2. Repo configurado (.mcp.json con jupyter) if [ -f .mcp.json ] && grep -q "jupyter" .mcp.json 2>/dev/null; then REPO_CONFIG=true log_ok "Repo configurado (.mcp.json tiene jupyter)" else log_warn "Repo no configurado para Jupyter" fi # 3. Jupyter corriendo en este repo if pgrep -af "jupyter" 2>/dev/null | grep -q "$PROJECT_PATH"; then JUPYTER_RUNNING=true CURRENT_PORT=$(pgrep -af "jupyter" | grep "$PROJECT_PATH" | grep -oP 'port[= ]\K\d+' | head -1) [ -z "$CURRENT_PORT" ] && CURRENT_PORT=$(cat .jupyter-port 2>/dev/null || echo "desconocido") log_ok "Jupyter corriendo en puerto $CURRENT_PORT" else log_warn "Jupyter no está corriendo" fi # 4. Reglas en CLAUDE.md if [ -f .claude/CLAUDE.md ] && grep -q "JUPYTER HABILITADO" .claude/CLAUDE.md 2>/dev/null; then RULES_OK=true log_ok "Reglas Jupyter en CLAUDE.md" else log_warn "Reglas Jupyter no configuradas" fi # 5. Instalación global (problemática) if [ -f ~/.local/bin/jupyter-mcp-server ]; then GLOBAL_MCP=true log_warn "Existe instalación GLOBAL de jupyter-mcp-server (se eliminará)" fi echo "" # ═══════════════════════════════════════════════════════════════ # DECISIÓN: ¿Ya está todo configurado? # ═══════════════════════════════════════════════════════════════ if $MCP_LOCAL && $REPO_CONFIG && $RULES_OK; then if $JUPYTER_RUNNING; then echo "═══════════════════════════════════════════════════════════════" log_ok "TODO LISTO - Jupyter configurado y corriendo en puerto $CURRENT_PORT" echo "═══════════════════════════════════════════════════════════════" echo "" echo "STATUS: READY" echo "PORT: $CURRENT_PORT" exit 0 else echo "═══════════════════════════════════════════════════════════════" log_ok "CONFIGURADO - Solo falta iniciar Jupyter" echo "═══════════════════════════════════════════════════════════════" echo "" echo "Ejecuta en otra terminal:" echo " cd $PROJECT_PATH && ./run-jupyter-lab.sh" echo "" echo "STATUS: CONFIGURED_NOT_RUNNING" exit 0 fi fi # ═══════════════════════════════════════════════════════════════ # PASO 2: ELIMINAR INSTALACIÓN GLOBAL SI EXISTE # ═══════════════════════════════════════════════════════════════ if $GLOBAL_MCP; then log_info "Eliminando instalación global de jupyter-mcp-server..." uv tool uninstall jupyter-mcp-server 2>/dev/null || true pip uninstall -y jupyter-mcp-server 2>/dev/null || true rm -f ~/.local/bin/jupyter-mcp-server if [ -f ~/.local/bin/jupyter-mcp-server ]; then log_error "No se pudo eliminar instalación global" else log_ok "Instalación global eliminada" fi fi # ═══════════════════════════════════════════════════════════════ # PASO 3: DETECTAR PUERTO LIBRE # ═══════════════════════════════════════════════════════════════ PUERTO=$(find_free_port) log_info "Puerto asignado: $PUERTO" # ═══════════════════════════════════════════════════════════════ # PASO 4: CREAR ESTRUCTURA DE CARPETAS # ═══════════════════════════════════════════════════════════════ log_info "Creando estructura de carpetas..." mkdir -p .claude notebooks log_ok "Carpetas .claude/ y notebooks/ creadas" # ═══════════════════════════════════════════════════════════════ # PASO 5: CREAR run-jupyter-lab.sh # ═══════════════════════════════════════════════════════════════ log_info "Creando run-jupyter-lab.sh..." cat > run-jupyter-lab.sh << 'SCRIPT' #!/bin/bash # Jupyter Lab para colaboración Claude + Usuario # Generado por /init-jupyter # REQUIERE: jupyter-collaboration instalado para que funcione el MCP # Autodetectar puerto libre find_free_port() { for port in 8888 8889 8890 8891 8892 8893 8894 8895 8896 8897 8898 8899; do if ! lsof -i:$port >/dev/null 2>&1; then echo $port return fi done echo 8888 } PORT=${1:-$(find_free_port)} # Guardar puerto para que Claude lo lea echo $PORT > .jupyter-port # Activar entorno si existe source .venv/bin/activate 2>/dev/null || true # Verificar que jupyter-collaboration está instalado if ! python -c "import jupyter_collaboration" 2>/dev/null; then echo "ERROR: jupyter-collaboration no está instalado" echo "Instala con: uv add jupyter-collaboration" exit 1 fi echo "══════════════════════════════════════════════════════" echo " Jupyter Lab + Colaboración iniciando en puerto $PORT" echo "══════════════════════════════════════════════════════" echo "" echo " Abre: http://localhost:$PORT" echo "" echo " Claude se conectará al kernel cuando abras un notebook" echo " Colaboración en tiempo real ACTIVADA" echo " Usa Ctrl+C para detener" echo "" echo "══════════════════════════════════════════════════════" jupyter lab \ --port=$PORT \ --no-browser \ --ServerApp.token='' \ --ServerApp.password='' \ --ServerApp.disable_check_xsrf=True \ --ServerApp.allow_origin='*' \ --ServerApp.root_dir="$(pwd)" \ --YDocExtension.ystore_class='ypy_websocket.ystore.TempFileYStore' \ --collaborative SCRIPT chmod +x run-jupyter-lab.sh log_ok "run-jupyter-lab.sh creado" # ═══════════════════════════════════════════════════════════════ # PASO 6: CREAR/ACTUALIZAR .mcp.json # ═══════════════════════════════════════════════════════════════ log_info "Configurando .mcp.json..." # Crear config temporal cat > /tmp/jupyter-mcp.json << EOF { "mcpServers": { "jupyter": { "command": "${PROJECT_PATH}/.venv/bin/jupyter-mcp-server", "args": [ "--runtime-url", "http://localhost:${PUERTO}", "--start-new-runtime", "false" ] } } } EOF # Merge o crear if [ -f .mcp.json ]; then if command -v jq >/dev/null 2>&1; then jq -s '.[0] * .[1] | .mcpServers = (.[0].mcpServers // {}) * (.[1].mcpServers // {})' \ .mcp.json /tmp/jupyter-mcp.json > .mcp.json.tmp mv .mcp.json.tmp .mcp.json log_ok ".mcp.json actualizado (merge)" else log_warn "jq no disponible, sobrescribiendo .mcp.json" mv /tmp/jupyter-mcp.json .mcp.json fi else mv /tmp/jupyter-mcp.json .mcp.json log_ok ".mcp.json creado" fi rm -f /tmp/jupyter-mcp.json # ═══════════════════════════════════════════════════════════════ # PASO 7: ACTUALIZAR .claude/CLAUDE.md # ═══════════════════════════════════════════════════════════════ log_info "Configurando reglas en .claude/CLAUDE.md..." JUPYTER_RULES='# JUPYTER HABILITADO EN ESTE REPO Este repositorio está configurado para exploración de datos con Jupyter + Claude. ## Estado del entorno - **Lanzador**: `./run-jupyter-lab.sh` - **Puerto config**: ver `.jupyter-port` o `.mcp.json` - **MCP**: jupyter-mcp-server configurado ## Reglas OBLIGATORIAS para Claude ### 1. SIEMPRE usar MCP jupyter para ejecutar código Python - Las ejecuciones se ven en tiempo real en Jupyter Lab del usuario - Compartimos variables y estado del kernel - **NUNCA usar bash para ejecutar Python en este repo** ### 2. Verificar Jupyter activo ANTES de ejecutar ```bash pgrep -af "jupyter" | grep "$(pwd)" || cat .jupyter-port 2>/dev/null ``` - Si no está activo → pedir al usuario: "Ejecuta `./run-jupyter-lab.sh` en otra terminal" ### 3. Gestión de notebooks - **TODOS los notebooks van a la carpeta `notebooks/`** o subcarpetas dentro de ella - Si un notebook tiene >50 celdas → crear uno nuevo - Nombrar descriptivamente: `notebooks/01_exploracion.ipynb`, `notebooks/02_limpieza.ipynb` - Mantener notebooks enfocados en una tarea ### 4. Gestión de Python - **SIEMPRE usar `uv` para gestionar Python** (entornos, dependencias, etc.) - Añadir paquetes con `uv add nombre_paquete` - Nunca usar pip directamente si uv está disponible ### 5. Antes de código pesado - Avisar al usuario - Usar `%%time` o `tqdm` para progreso --- ' if [ -f .claude/CLAUDE.md ]; then if ! grep -q "JUPYTER HABILITADO" .claude/CLAUDE.md; then # Prepend echo "$JUPYTER_RULES" > .claude/CLAUDE.md.tmp cat .claude/CLAUDE.md >> .claude/CLAUDE.md.tmp mv .claude/CLAUDE.md.tmp .claude/CLAUDE.md log_ok "Reglas añadidas al principio de CLAUDE.md" else log_ok "Reglas ya existían en CLAUDE.md" fi else echo "$JUPYTER_RULES" > .claude/CLAUDE.md log_ok "CLAUDE.md creado con reglas" fi # ═══════════════════════════════════════════════════════════════ # PASO 8: INICIALIZAR ENTORNO PYTHON E INSTALAR DEPENDENCIAS # ═══════════════════════════════════════════════════════════════ log_info "Configurando entorno Python..." # Crear venv si no existe if [ ! -d .venv ]; then log_info "Creando venv..." uv venv 2>/dev/null || python -m venv .venv fi # Activar venv source .venv/bin/activate 2>/dev/null || true # Instalar dependencias log_info "Instalando dependencias (jupyter, mcp, colaboración)..." if command -v uv >/dev/null 2>&1; then # Inicializar pyproject.toml si no existe [ ! -f pyproject.toml ] && uv init 2>/dev/null || true uv add jupyter jupyterlab jupyter-collaboration jupyter-mcp-server pandas numpy matplotlib 2>/dev/null || \ pip install jupyter jupyterlab jupyter-collaboration jupyter-mcp-server pandas numpy matplotlib else pip install jupyter jupyterlab jupyter-collaboration jupyter-mcp-server pandas numpy matplotlib fi # Verificar instalación echo "" if [ -f .venv/bin/jupyter-mcp-server ]; then log_ok "jupyter-mcp-server instalado en venv local" else log_error "jupyter-mcp-server NO instalado" fi if python -c "import jupyter_collaboration" 2>/dev/null; then log_ok "jupyter-collaboration instalado" else log_warn "jupyter-collaboration no instalado" fi # ═══════════════════════════════════════════════════════════════ # RESUMEN FINAL # ═══════════════════════════════════════════════════════════════ echo "" echo "═══════════════════════════════════════════════════════════════" echo " CONFIGURACIÓN COMPLETADA" echo "═══════════════════════════════════════════════════════════════" echo "" echo " Proyecto: $PROJECT_PATH" echo " Puerto: $PUERTO" echo "" echo " Archivos creados/actualizados:" echo " ✓ run-jupyter-lab.sh" echo " ✓ .mcp.json" echo " ✓ .claude/CLAUDE.md" echo " ✓ .venv/bin/jupyter-mcp-server" echo "" echo " ┌─────────────────────────────────────────────────────────┐" echo " │ PASOS SIGUIENTES: │" echo " │ │" echo " │ 1. SALIR de Claude Code: /exit o Ctrl+C │" echo " │ (necesario para cargar el nuevo MCP) │" echo " │ │" echo " │ 2. En OTRA terminal: │" echo " │ cd $PROJECT_PATH && ./run-jupyter-lab.sh │" echo " │ │" echo " │ 3. Volver a entrar a Claude: │" echo " │ cd $PROJECT_PATH && claude │" echo " │ │" echo " │ 4. Abrir en navegador: http://localhost:$PUERTO │" echo " └─────────────────────────────────────────────────────────┘" echo "" echo "STATUS: CONFIGURED" echo "PORT: $PUERTO"