#!/bin/bash # Start the jobbsok-tools stdio MCP server on an interpreter that is new enough. # # bash 3.2-clean and ASCII-only on purpose: the system bash on this Mac is 3.2, # and a multibyte character has crashed a `set -u` script here before. # # Why this file exists at all, rather than .mcp.json naming python3 directly: # a GUI-spawned process on this Mac can resolve python3 to 3.9.6 under an # empty launchd PATH (risk H2). A server running on 3.9.6 is worse than no # server, because the degradation branch already covers an absent server and # nothing covers a silently wrong one. So every candidate below is gated on # the version, and when none passes the launcher refuses instead of falling # through to whatever python3 the PATH happens to offer. # # Resolution order, first candidate that passes the gate wins: # 1. $JOBBSOK_PYTHON -- the manual override. Named explicitly by # the operator, so a version failure here # is refused outright rather than skipped: # falling back from an interpreter someone # asked for would hide their mistake. # 2. $CLAUDE_PLUGIN_DATA/venv -- what scripts/bootstrap.sh builds against # an installed plugin. This is the # supported route in an installed copy. # 3. $CLAUDE_PLUGIN_ROOT/.venv -- the development environment. The packaged # archive excludes the virtualenv, so this # branch never fires in an installed copy. # 4. python3 on PATH -- last resort, and only at 3.10 or newer. set -eu MIN_MAJOR=3 MIN_MINOR=10 SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd) if [ -n "${CLAUDE_PLUGIN_ROOT:-}" ]; then PLUGIN_ROOT="$CLAUDE_PLUGIN_ROOT" else PLUGIN_ROOT=$(cd "$SCRIPT_DIR/.." && pwd) fi SERVER="${PLUGIN_ROOT}/scripts/jobbsok_tools.py" version_ok() { "$1" -c "import sys; sys.exit(0 if sys.version_info >= ($MIN_MAJOR, $MIN_MINOR) else 1)" \ >/dev/null 2>&1 } version_of() { "$1" -c "import sys; print('%d.%d.%d' % sys.version_info[:3])" 2>/dev/null || echo "ukjent" } usable() { [ -n "$1" ] || return 1 command -v "$1" >/dev/null 2>&1 || return 1 version_ok "$1" } PYTHON_BIN="" if [ -n "${JOBBSOK_PYTHON:-}" ]; then if ! command -v "$JOBBSOK_PYTHON" >/dev/null 2>&1; then echo "jobbsok-tools: JOBBSOK_PYTHON=$JOBBSOK_PYTHON is not executable." >&2 echo "jobbsok-tools: refusing to start. Point it at a Python ${MIN_MAJOR}.${MIN_MINOR}+ interpreter." >&2 exit 1 fi if ! version_ok "$JOBBSOK_PYTHON"; then echo "jobbsok-tools: JOBBSOK_PYTHON=$JOBBSOK_PYTHON is Python $(version_of "$JOBBSOK_PYTHON")." >&2 echo "jobbsok-tools: ${MIN_MAJOR}.${MIN_MINOR}+ is required; refusing to start on it." >&2 exit 1 fi PYTHON_BIN="$JOBBSOK_PYTHON" fi if [ -z "$PYTHON_BIN" ] && [ -n "${CLAUDE_PLUGIN_DATA:-}" ]; then CANDIDATE="${CLAUDE_PLUGIN_DATA}/venv/bin/python" if usable "$CANDIDATE"; then PYTHON_BIN="$CANDIDATE" fi fi if [ -z "$PYTHON_BIN" ]; then CANDIDATE="${PLUGIN_ROOT}/.venv/bin/python3" if usable "$CANDIDATE"; then PYTHON_BIN="$CANDIDATE" fi fi if [ -z "$PYTHON_BIN" ]; then CANDIDATE=$(command -v python3 2>/dev/null || true) if usable "$CANDIDATE"; then PYTHON_BIN="$CANDIDATE" fi fi if [ -z "$PYTHON_BIN" ]; then echo "jobbsok-tools: found no Python ${MIN_MAJOR}.${MIN_MINOR}+ interpreter." >&2 echo "jobbsok-tools: tried JOBBSOK_PYTHON, \$CLAUDE_PLUGIN_DATA/venv, \$CLAUDE_PLUGIN_ROOT/.venv and python3 on PATH." >&2 echo "jobbsok-tools: run 'bash scripts/bootstrap.sh' against the installed plugin, or set JOBBSOK_PYTHON." >&2 echo "jobbsok-tools: refusing to start rather than serving on an interpreter below ${MIN_MAJOR}.${MIN_MINOR}." >&2 exit 1 fi if [ ! -f "$SERVER" ]; then echo "jobbsok-tools: server not found at $SERVER" >&2 exit 1 fi exec "$PYTHON_BIN" "$SERVER" "$@"