Files
state-hub/scripts/register-mcp.sh

152 lines
3.5 KiB
Bash
Executable File

#!/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/tcp/$host/$port" >/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."