#!/usr/bin/env bash set -euo pipefail STATE_HUB_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" CLAUDE_JSON="${CLAUDE_JSON:-$HOME/.claude.json}" SERVER_NAME="${STATE_HUB_MCP_NAME:-state-hub}" API_BASE="${API_BASE:-}" MCP_URL="${MCP_URL:-}" DRY_RUN=0 usage() { cat <<'USAGE' Usage: scripts/register-mcp.sh [--url URL] [--api-base URL] [--dry-run] Registers the State Hub MCP server for Claude Code. Options: --url URL MCP SSE URL to register. Defaults to local :8001 or tunnel :18001. --api-base URL State Hub API URL used for reachability checks. --dry-run Print what would happen without changing Claude config. -h, --help Show this help. USAGE } while [ "$#" -gt 0 ]; do case "$1" in --url) MCP_URL="${2:-}" shift 2 ;; --api-base) API_BASE="${2:-}" shift 2 ;; --dry-run) DRY_RUN=1 shift ;; -h|--help) usage exit 0 ;; *) echo "ERROR: unknown argument: $1" >&2 usage >&2 exit 2 ;; esac done status() { printf '%s\n' "$*" } api_healthy() { local base="$1" curl -fsS --max-time 2 "${base%/}/state/health" >/dev/null 2>&1 } port_open() { local host="$1" local port="$2" timeout 2 bash -c ":/dev/null 2>&1 } if [ -z "$API_BASE" ]; then if api_healthy "http://127.0.0.1:8000"; then API_BASE="http://127.0.0.1:8000" elif api_healthy "http://127.0.0.1:18000"; then API_BASE="http://127.0.0.1:18000" else API_BASE="http://127.0.0.1:8000" fi fi if [ -z "$MCP_URL" ]; then if port_open 127.0.0.1 8001; then MCP_URL="http://127.0.0.1:8001/sse" elif port_open 127.0.0.1 18001; then MCP_URL="http://127.0.0.1:18001/sse" elif [ "$API_BASE" = "http://127.0.0.1:18000" ]; then MCP_URL="http://127.0.0.1:18001/sse" else MCP_URL="http://127.0.0.1:8001/sse" fi fi CONFIG="$(python3 - "$MCP_URL" <<'PY' import json import sys print(json.dumps({"type": "sse", "url": sys.argv[1]}, separators=(",", ":"))) PY )" status "State Hub directory: $STATE_HUB_DIR" status "API health check: ${API_BASE%/}/state/health" status "MCP registration: $SERVER_NAME -> $MCP_URL" if api_healthy "$API_BASE"; then status "OK: State Hub API is reachable." else status "WARN: State Hub API is not reachable at ${API_BASE%/}/state/health." status " Start it with 'make api' or bring up the ops-bridge tunnel." fi if ! command -v claude >/dev/null 2>&1; then if [ "$DRY_RUN" -eq 1 ]; then status "WARN: claude CLI not found on PATH; dry-run will still show the command." else status "ERROR: claude CLI not found on PATH." status " Install or expose Claude Code CLI, then rerun: make register-mcp" exit 1 fi fi CURRENT_URL="$(python3 - "$CLAUDE_JSON" "$SERVER_NAME" <<'PY' import json import sys from pathlib import Path path = Path(sys.argv[1]) name = sys.argv[2] if not path.exists(): print("") raise SystemExit try: data = json.loads(path.read_text()) except json.JSONDecodeError: print("") raise SystemExit entry = data.get("mcpServers", {}).get(name, {}) print(entry.get("url", "")) PY )" if [ "$CURRENT_URL" = "$MCP_URL" ]; then status "OK: $SERVER_NAME is already registered with this URL." exit 0 fi if [ "$DRY_RUN" -eq 1 ]; then status "DRY-RUN: would run:" status " claude mcp add-json -s user $SERVER_NAME '$CONFIG'" exit 0 fi claude mcp add-json -s user "$SERVER_NAME" "$CONFIG" status "OK: registered $SERVER_NAME." status "Restart Claude Code so the MCP server list is refreshed."