-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathinstall.sh
More file actions
136 lines (117 loc) · 5.18 KB
/
Copy pathinstall.sh
File metadata and controls
136 lines (117 loc) · 5.18 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
#!/bin/sh
# UTMStack MCP server installer (macOS / Linux)
#
# curl -fsSL https://raw.githubusercontent.com/utmstack/MCP/main/install.sh | sh
#
# Downloads the release build for this platform, verifies its SHA256 against
# the checksums published with the release, and installs it.
set -eu
REPO="utmstack/MCP"
APP="utmstack-mcp"
VERSION="${UTMSTACK_MCP_VERSION:-latest}"
info() { printf '%s\n' "$*" >&2; }
error() { printf 'error: %s\n' "$*" >&2; exit 1; }
# --- platform ------------------------------------------------------------- #
os=$(uname -s)
arch=$(uname -m)
case "$os" in
Darwin) os_tag="darwin" ;;
Linux) os_tag="linux" ;;
# Git Bash / MSYS / Cygwin. There is a native PowerShell installer; sending
# users there beats a bare "unsupported OS", and this is the path the CLI
# installer reaches when it tries to add the SIEM tools on Windows.
MINGW*|MSYS*|CYGWIN*)
info "Windows detected. Install with PowerShell instead:"
info " irm https://raw.githubusercontent.com/${REPO}/main/install.ps1 | iex"
exit 1
;;
*) error "unsupported operating system: $os (build from source instead)" ;;
esac
case "$arch" in
x86_64|amd64) arch_tag="amd64" ;;
arm64|aarch64) arch_tag="arm64" ;;
*) error "unsupported architecture: $arch" ;;
esac
asset="${APP}-${os_tag}-${arch_tag}.tar.gz"
# musl systems cannot run the glibc build.
if [ "$os_tag" = "linux" ] && ldd --version 2>&1 | grep -qi musl; then
error "musl libc detected (Alpine?). These builds are glibc-only; install from source."
fi
command -v curl >/dev/null 2>&1 || error "curl is required"
command -v tar >/dev/null 2>&1 || error "tar is required"
# --- resolve release ------------------------------------------------------ #
if [ "$VERSION" = "latest" ]; then
api="https://api.github.com/repos/${REPO}/releases/latest"
else
api="https://api.github.com/repos/${REPO}/releases/tags/${VERSION}"
fi
info "Resolving ${VERSION} release of ${REPO} ..."
tag=$(curl -fsSL "$api" | sed -n 's/.*"tag_name": *"\([^"]*\)".*/\1/p' | head -1)
[ -n "$tag" ] || error "could not resolve a release (is the repository public and does it have a release?)"
info " version: $tag"
base="https://github.com/${REPO}/releases/download/${tag}"
tmp=$(mktemp -d)
cleanup() { rm -rf "$tmp"; }
trap cleanup EXIT INT TERM
# --- download + verify ---------------------------------------------------- #
info "Downloading ${asset} ..."
curl -fsSL "${base}/${asset}" -o "${tmp}/${asset}" \
|| error "no build published for ${os_tag}/${arch_tag} in ${tag}"
info "Verifying checksum ..."
curl -fsSL "${base}/checksums.txt" -o "${tmp}/checksums.txt" \
|| error "could not download checksums.txt — refusing to install unverified binary"
expected=$(grep " ${asset}\$" "${tmp}/checksums.txt" | awk '{print $1}' | head -1)
[ -n "$expected" ] || error "no checksum listed for ${asset} — refusing to install"
if command -v sha256sum >/dev/null 2>&1; then
actual=$(sha256sum "${tmp}/${asset}" | awk '{print $1}')
elif command -v shasum >/dev/null 2>&1; then
actual=$(shasum -a 256 "${tmp}/${asset}" | awk '{print $1}')
else
error "need sha256sum or shasum to verify the download"
fi
[ "$expected" = "$actual" ] || error "checksum mismatch — expected ${expected}, got ${actual}"
info " checksum OK"
# --- install -------------------------------------------------------------- #
# Pick a writable prefix, preferring a system location but never demanding sudo.
if [ -w /usr/local/lib ] 2>/dev/null; then
lib_dir="/usr/local/lib/${APP}"; bin_dir="/usr/local/bin"
else
lib_dir="${HOME}/.local/lib/${APP}"; bin_dir="${HOME}/.local/bin"
fi
mkdir -p "$lib_dir" "$bin_dir"
info "Installing to ${lib_dir} ..."
tar -xzf "${tmp}/${asset}" -C "$tmp"
rm -rf "${lib_dir:?}"/*
# The archive contains a single top-level directory.
src=$(find "$tmp" -maxdepth 1 -type d -name "${APP}*" | head -1)
[ -n "$src" ] || error "unexpected archive layout"
cp -R "${src}/." "${lib_dir}/"
chmod +x "${lib_dir}/${APP}"
ln -sf "${lib_dir}/${APP}" "${bin_dir}/${APP}"
# Clear macOS quarantine if the files picked it up. Newer macOS releases
# dropped xattr's -r flag, and xattr prints its usage to STDOUT on bad
# arguments, so the recursive form both fails and spams the installer output.
# find + per-file -d works on every version.
if [ "$os_tag" = "darwin" ] && command -v xattr >/dev/null 2>&1; then
find "$lib_dir" -exec xattr -d com.apple.quarantine {} \; >/dev/null 2>&1 || true
fi
installed=$("${bin_dir}/${APP}" --version 2>/dev/null || echo "unknown")
info ""
info "Installed ${installed}"
info " binary: ${bin_dir}/${APP}"
case ":${PATH}:" in
*":${bin_dir}:"*) ;;
*)
info ""
info "NOTE: ${bin_dir} is not on your PATH. Add this to your shell profile:"
info " export PATH=\"${bin_dir}:\$PATH\""
;;
esac
info ""
info "Next steps:"
info " 1. ${APP} init # configure your UTMStack connection"
info " 2. Register with a client:"
info " Claude Code: claude mcp add --scope user utmstack -- ${APP}"
info " OpenCode: add to ~/.config/opencode/opencode.json:"
info " {\"mcp\":{\"utmstack\":{\"type\":\"local\",\"command\":[\"${APP}\"],\"enabled\":true}}}"
info ""