#!/usr/bin/env bash # mxclaude-desktop — configure Claude Desktop to use MiniMax as inference backend. # # Edits ~/Library/Application Support/Claude-3p/configLibrary/{_meta,}.json # and restarts Claude Desktop. macOS only. # # Usage: # mxclaude-desktop # configure to MiniMax-M3 and restart # mxclaude-desktop update # git pull latest from the repo # mxclaude-desktop -h # help # # Reads MINIMAX_API_KEY from env or ~/.zshrc & friends (else prompts). # Base URL defaults to https://api.minimaxi.com/anthropic; override with MINIMAX_BASE_URL. # # Get your API key (requires Coding Plan / Token Plan): https://platform.minimaxi.com set -euo pipefail CONFIG_DIR="$HOME/Library/Application Support/Claude-3p/configLibrary" META="$CONFIG_DIR/_meta.json" ENTRY_NAME="mxclaude-desktop" AUTH_SCHEME="bearer" MAIN_MODEL="MiniMax-M3" FAST_MODEL="MiniMax-M2.5" BASE_URL="${MINIMAX_BASE_URL:-https://api.minimaxi.com/anthropic}" while [[ $# -gt 0 ]]; do case "$1" in -h|--help) sed -n '2,16p' "$0" | sed 's/^# \{0,1\}//'; exit 0 ;; update) SELF_DIR="$(cd "$(dirname "$0")" && pwd)" echo "mxclaude-desktop: pulling latest from $SELF_DIR ..." git -C "$SELF_DIR" pull && echo "mxclaude-desktop: updated." || { echo "mxclaude-desktop: git pull failed." >&2; exit 1 } exit 0 ;; *) echo "mxclaude-desktop: unknown argument: $1" >&2; echo "Run 'mxclaude-desktop -h' for usage." >&2; exit 2 ;; esac done preflight() { if [[ "$(uname)" != "Darwin" ]]; then echo "mxclaude-desktop: macOS only." >&2; exit 1; fi if [[ ! -d "/Applications/Claude.app" ]]; then echo "mxclaude-desktop: /Applications/Claude.app not found." >&2; exit 1; fi if [[ ! -x "/usr/bin/jq" ]]; then echo "mxclaude-desktop: /usr/bin/jq not found." >&2; exit 1; fi local dev_settings="$HOME/Library/Application Support/Claude/developer_settings.json" if [[ ! -f "$dev_settings" ]] || ! /usr/bin/jq -e '.allowDevTools == true' "$dev_settings" >/dev/null 2>&1; then cat >&2 <<'EOF' mxclaude-desktop: Developer Mode not enabled in Claude Desktop. Enable it once: Help → Troubleshooting → Enable Developer Mode, then re-run. EOF exit 1 fi } resolve_api_key() { if [[ -n "${MINIMAX_API_KEY:-}" ]]; then printf '%s' "$MINIMAX_API_KEY"; return 0; fi local rc found="" for rc in "$HOME/.zshrc" "$HOME/.bashrc" "$HOME/.bash_profile" "$HOME/.profile"; do [[ -r "$rc" ]] || continue found="$(grep -v '^[[:space:]]*#' "$rc" 2>/dev/null | grep -E '^[[:space:]]*export[[:space:]]+MINIMAX_API_KEY=' | tail -1 | sed -E 's/^[^=]*=//; s/^"(.*)"$/\1/; s/^'\''(.*)'\''$/\1/' || true)" if [[ -n "$found" ]]; then printf '%s' "$found"; return 0; fi done local key key="$(osascript 2>/dev/null <<'APPLESCRIPT' || true try set theKey to text returned of (display dialog "MiniMax API Key not found.\nPaste your key:" default answer "" with hidden answer with title "mxclaude-desktop") return theKey end try APPLESCRIPT )" if [[ -z "$key" ]]; then echo "mxclaude-desktop: no API Key provided. Aborting." >&2; exit 1; fi printf '%s' "$key" } confirm_or_abort() { echo; echo "About to: $1"; echo "Press Enter to continue, Ctrl-C to abort."; read -r _; } write_entry() { local uuid="$1"; local entry_path="$CONFIG_DIR/${uuid}.json"; local tmp="${entry_path}.tmp" local content content="$(jq -n --arg baseUrl "$BASE_URL" --arg apiKey "$MX_KEY" --arg auth "$AUTH_SCHEME" --arg main "$MAIN_MODEL" --arg fast "$FAST_MODEL" \ '{inferenceProvider:"gateway",inferenceGatewayBaseUrl:$baseUrl,inferenceGatewayApiKey:$apiKey,inferenceGatewayAuthScheme:$auth,unstableDisableModelVerification:true,inferenceModels:[{name:$main,supports1m:false},{name:$fast,supports1m:false}]}')" printf '%s' "$content" > "$tmp"; chmod 600 "$tmp"; mv "$tmp" "$entry_path" } ensure_meta_entry() { mkdir -p "$CONFIG_DIR" local existing_uuid="" if [[ -f "$META" ]]; then existing_uuid="$(jq -r --arg name "$ENTRY_NAME" '.entries[]?|select(.name==$name)|.id' "$META" 2>/dev/null | head -1)"; fi local uuid if [[ -n "$existing_uuid" ]]; then uuid="$existing_uuid"; else uuid="$(uuidgen | tr 'A-Z' 'a-z')"; fi local tmp="${META}.tmp" content if [[ -f "$META" ]]; then content="$(jq --arg id "$uuid" --arg name "$ENTRY_NAME" '.appliedId=$id|.entries=((.entries//[])|map(select(.name!=$name))+[{id:$id,name:$name}])' "$META")" else content="$(jq -n --arg id "$uuid" --arg name "$ENTRY_NAME" '{appliedId:$id,entries:[{id:$id,name:$name}]}')" fi printf '%s' "$content" > "$tmp"; chmod 600 "$tmp"; mv "$tmp" "$META" printf '%s' "$uuid" } restart_claude() { killall Claude 2>/dev/null || true; sleep 1; open -a Claude; } preflight MX_KEY="$(resolve_api_key)" confirm_or_abort "configure Claude Desktop to use MiniMax ($BASE_URL) and restart it." UUID="$(ensure_meta_entry)" write_entry "$UUID" restart_claude cat <<'EOF' Done. Claude Desktop is restarting with MiniMax as the inference backend. Re-run mxclaude-desktop any time to refresh the gateway config. EOF