#!/bin/sh # Enable the opt-in MCP link to "mother". # # Direction: this agent's Pi calls mother's tools. colibri-mcp dials OUT to # mother over SSH-stdio and proxies mother's MCP tools to the Pi via its # external-call path. Off by default; this script turns it on. # # Key exchange is vault-mediated: this node publishes its colibri pubkey to # Vaultwarden (item `hive-pubkey-`), and the mother node's # `mother-sync-hive-keys` rebuilds its authorized_keys from those items. The # pubkey is also printed as a manual fallback. # # The colibri daemon (and the Pi it spawns) run as the `colibri` user, so the # outbound SSH uses an identity in the colibri home (/var/db/colibri/.ssh). set -eu PROVIDER_ENV="/usr/local/etc/colibri/provider.env" EXTERNAL_MCP="/usr/local/etc/colibri/external-mcp.json" COLIBRI_HOME="/var/db/colibri" SSH_KEY="${COLIBRI_HOME}/.ssh/id_ed25519" finish() { echo "" echo "Press Enter to close." read -r _ exit "${1:-0}" } have() { command -v "$1" >/dev/null 2>&1 } # Publish the colibri pubkey to Vaultwarden as hive-pubkey- so a # mother node can authorize it automatically (mother-sync-hive-keys). bw runs as # root so it can read the BW_* bootstrap creds from provider.env; the pubkey is # public, so passing it through the environment is fine. Returns non-zero on any # failure so the caller can fall back to the printed key. publish_pubkey() { mdo -u root env \ HIVE_ITEM="hive-pubkey-$(hostname)" \ HIVE_PUB="$1" \ PROVIDER_ENV="$PROVIDER_ENV" \ sh -s <<'PUBLISH' set -eu command -v bw >/dev/null 2>&1 || { echo "bw not found" >&2; exit 4; } . "$PROVIDER_ENV" [ -n "${BW_CLIENTID:-}" ] && [ -n "${BW_CLIENTSECRET:-}" ] && [ -n "${BW_PASSWORD:-}" ] || { echo "provider.env lacks BW_* creds" >&2; exit 1; } bw logout >/dev/null 2>&1 || true bw login --apikey >/dev/null 2>&1 || true SESS="$(bw unlock --passwordenv BW_PASSWORD --raw 2>/dev/null)" || { echo "bw unlock failed" >&2; exit 1; } [ -n "$SESS" ] || { echo "empty bw session" >&2; exit 1; } bw sync --session "$SESS" >/dev/null 2>&1 || true ITEM_ID="$(bw list items --search "$HIVE_ITEM" --session "$SESS" 2>/dev/null | python3 -c "import sys,json,os n=os.environ['HIVE_ITEM'] try: d=json.load(sys.stdin) except Exception: d=[] print(next((i['id'] for i in d if i.get('name')==n), ''))")" if [ -n "$ITEM_ID" ]; then bw get item "$ITEM_ID" --session "$SESS" | python3 -c "import sys,json,os,base64 d=json.load(sys.stdin); d['notes']=os.environ['HIVE_PUB'] sys.stdout.write(base64.b64encode(json.dumps(d).encode()).decode())" | bw edit item "$ITEM_ID" --session "$SESS" >/dev/null else python3 -c "import json,os,base64 print(base64.b64encode(json.dumps({'type':2,'name':os.environ['HIVE_ITEM'],'notes':os.environ['HIVE_PUB']}).encode()).decode())" | bw create item --session "$SESS" >/dev/null fi bw lock >/dev/null 2>&1 || true PUBLISH } if ! have mdo; then echo "ERROR: mdo is required to update the colibri service configuration." finish 1 fi echo "========================================" echo " Clawdie — Enable Mother Link" echo "========================================" echo "" # 0. Mother's reachable address + remote MCP command. printf " Mother SSH target (user@tailscale-ip-or-name): " read -r MOTHER_HOST if [ -z "${MOTHER_HOST:-}" ]; then echo " Cancelled: no mother target entered." finish 0 fi printf " Mother MCP command [colibri-mcp]: " read -r MOTHER_CMD [ -n "${MOTHER_CMD:-}" ] || MOTHER_CMD="colibri-mcp" # 1. Ensure the colibri service account has an SSH key for the outbound link. echo "" echo "[1/5] Ensuring colibri SSH identity..." mdo -u root sh -c ' set -eu home="$1"; key="$2" install -d -o colibri -g colibri -m 0700 "${home}/.ssh" if [ ! -f "$key" ]; then ssh-keygen -t ed25519 -N "" -f "$key" -C "colibri@$(hostname)" >/dev/null chown colibri:colibri "$key" "${key}.pub" chmod 0600 "$key"; chmod 0644 "${key}.pub" fi ' sh "$COLIBRI_HOME" "$SSH_KEY" # 2. Publish the pubkey to Vaultwarden so mother can authorize it. echo "[2/5] Publishing pubkey to Vaultwarden..." PUBKEY="$(mdo -u root cat "${SSH_KEY}.pub" 2>/dev/null || true)" if [ -z "$PUBKEY" ]; then echo " WARNING: could not read ${SSH_KEY}.pub; skipping publish." elif have bw && publish_pubkey "$PUBKEY"; then echo " Published as hive-pubkey-$(hostname); mother will pick it up on sync." else echo " NOTE: vault publish unavailable; authorize the printed key manually." fi # 3. Merge the mother entry into the external MCP registry. Use jq so existing # server entries are preserved (not overwritten), and write atomically. echo "[3/5] Registering mother in ${EXTERNAL_MCP}..." if ! have jq; then echo " ERROR: jq is required to merge the mother entry into ${EXTERNAL_MCP}." finish 1 fi existing="$(mdo -u root cat "$EXTERNAL_MCP" 2>/dev/null || echo '{}')" [ -n "$existing" ] || existing='{}' printf '%s\n' "$existing" | jq --arg host "$MOTHER_HOST" --arg cmd "$MOTHER_CMD" --arg key "$SSH_KEY" \ '.servers = ((.servers // {}) + {mother: {command: "ssh", args: ["-i", $key, "-o", "BatchMode=yes", "-o", "StrictHostKeyChecking=accept-new", $host, $cmd], env: {}}})' | mdo -u root sh -c ' set -eu f="$1" tmp="$(mktemp "$(dirname "$f")/external-mcp.XXXXXX")" cat >"$tmp" chmod 0644 "$tmp" mv "$tmp" "$f" ' sh "$EXTERNAL_MCP" # 4. Allow external MCP calls: upsert COLIBRI_MCP_EXTERNAL_CALL=1 into provider.env. echo "[4/5] Enabling external MCP calls..." mdo -u root sh -c ' set -eu f="$1" tmp="$(mktemp "$(dirname "$f")/.provider.env.XXXXXX")" grep -v "^COLIBRI_MCP_EXTERNAL_CALL=" "$f" >"$tmp" 2>/dev/null || : printf "COLIBRI_MCP_EXTERNAL_CALL=\"1\"\n" >>"$tmp" chmod 0600 "$tmp" mv "$tmp" "$f" ' sh "$PROVIDER_ENV" # 5. Restart the daemon so the Pi inherits the new env + registry. echo "[5/5] Restarting colibri daemon..." mdo -u root service colibri_daemon restart >/dev/null 2>&1 || echo " WARNING: restart failed; run: mdo -u root service colibri_daemon restart" echo "" echo "Mother link configured. If the vault publish succeeded, mother authorizes" echo "this key automatically on its next sync. Manual fallback — add to mother's" echo "authorized_keys, restricted to the MCP command:" echo "" echo " command=\"${MOTHER_CMD}\",restrict,no-pty $(mdo -u root cat "${SSH_KEY}.pub" 2>/dev/null || echo '')" echo "" echo "Once authorized on mother, the Pi can call mother's tools via colibri-mcp." finish 0