# write_mcp_jupyter_config # ------------------------- # Genera o actualiza .mcp.json con la configuracion de jupyter-mcp-server para un # analisis/proyecto. La entrada `jupyter` usa el wrapper `jupyter_mcp_serve.sh` # (no el console-script directo), de modo que el MCP SIEMPRE tiene servidor: el # wrapper arranca (o reusa) un Jupyter Lab en el puerto indicado usando el venv # del propio analisis y lo engancha al MCP por stdio. # # Por que el wrapper y no el console-script directo: el console-script # `jupyter-mcp-server --jupyter-url http://localhost:PORT` solo se CONECTA, no # arranca Jupyter. Si el server no esta levantado, el MCP responde `initialize` # pero no hay kernel y toda operacion sobre notebooks falla. El wrapper levanta el # server con el venv correcto (JUPYTER_MCP_VENV) antes de exec del MCP, asi que # abrir Claude desde el analisis basta — no hace falta lanzar run-jupyter-lab.sh # aparte. Si ya hay un Jupyter en ese puerto (p.ej. run-jupyter-lab.sh), lo reusa. # # Env overrides que se inyectan al wrapper (ver jupyter_mcp_serve.sh): # JUPYTER_MCP_VENV venv del analisis (su .venv, con jupyter + jupyter-mcp-server) # JUPYTER_MCP_ROOT root de notebooks = directorio del analisis # JUPYTER_MCP_PORT puerto del Jupyter gestionado # JUPYTER_MCP_TOKEN token (vacio: solo escucha en 127.0.0.1) # # GOTCHA (2026-05-28): `python -m jupyter_mcp_server.server` NO arranca nada — # server.py no tiene bloque __main__. El entrypoint real es el console-script # `jupyter-mcp-server` (que el wrapper localiza dentro del venv del analisis). # # USO (sourced): # source write_mcp_jupyter_config.sh # write_mcp_jupyter_config /path/to/analysis 8890 write_mcp_jupyter_config() { local project_dir="${1:-.}" local port="${2:-8888}" local mcp_file="${project_dir}/.mcp.json" local abs_project abs_project="$(cd "$project_dir" && pwd)" local python_bin="${abs_project}/.venv/bin/python" local mcp_bin="${abs_project}/.venv/bin/jupyter-mcp-server" if [ ! -f "$python_bin" ]; then echo "write_mcp_jupyter_config: python no encontrado en ${python_bin}" >&2 return 1 fi # Verificar que el console-script esta instalado en el venv del analisis if [ ! -x "$mcp_bin" ]; then echo "write_mcp_jupyter_config: jupyter-mcp-server no instalado en el venv (${mcp_bin}). Instala con: uv pip install jupyter-mcp-server" >&2 return 1 fi # Localizar el wrapper jupyter_mcp_serve.sh subiendo desde el directorio del # analisis hasta la raiz del repo. Fallback a FN_REGISTRY_ROOT. local wrapper="" d="$abs_project" local i for i in 1 2 3 4 5 6 7 8; do if [ -f "$d/bash/functions/infra/jupyter_mcp_serve.sh" ]; then wrapper="$d/bash/functions/infra/jupyter_mcp_serve.sh" break fi d="$(dirname "$d")" [ "$d" = "/" ] && break done if [ -z "$wrapper" ] && [ -n "${FN_REGISTRY_ROOT:-}" ] && [ -f "${FN_REGISTRY_ROOT}/bash/functions/infra/jupyter_mcp_serve.sh" ]; then wrapper="${FN_REGISTRY_ROOT}/bash/functions/infra/jupyter_mcp_serve.sh" fi if [ -z "$wrapper" ]; then echo "write_mcp_jupyter_config: no encuentro bash/functions/infra/jupyter_mcp_serve.sh subiendo desde ${abs_project} ni en FN_REGISTRY_ROOT" >&2 return 1 fi local new_config new_config=$(cat << EOF { "mcpServers": { "jupyter": { "command": "bash", "args": [ "${wrapper}" ], "env": { "JUPYTER_MCP_VENV": "${abs_project}/.venv", "JUPYTER_MCP_ROOT": "${abs_project}", "JUPYTER_MCP_PORT": "${port}", "JUPYTER_MCP_TOKEN": "" } } } } EOF ) if [ -f "$mcp_file" ] && command -v jq &>/dev/null; then # Merge conservando otros servidores MCP. Usa `+` (shallow) en el mapa de # servidores para REEMPLAZAR la entrada `jupyter` entera — `*` (deep) dejaba # keys huerfanas de configs viejas (ej. flags `args` obsoletos). jq -s '.[0] * {mcpServers: ((.[0].mcpServers // {}) + (.[1].mcpServers // {}))}' \ "$mcp_file" <(echo "$new_config") > "${mcp_file}.tmp" mv "${mcp_file}.tmp" "$mcp_file" else echo "$new_config" > "$mcp_file" fi echo "$mcp_file" }