301 lines
9.7 KiB
Bash
Executable file
301 lines
9.7 KiB
Bash
Executable file
#!/bin/bash
|
||
#
|
||
# Install Hindsight MCP server for Claude Desktop
|
||
#
|
||
# Usage:
|
||
# curl -fsSL https://hindsight.vectorize.io/get-mcp | bash -s -- --app claude-desktop --set HINDSIGHT_API_LLM_API_KEY=YOUR_KEY
|
||
#
|
||
# Options:
|
||
# --app Required. Target application (currently only: claude-desktop)
|
||
# --set ENV=VALUE Set environment variable (can be repeated)
|
||
#
|
||
# Examples:
|
||
# # With OpenAI
|
||
# curl -fsSL https://hindsight.vectorize.io/get-mcp | bash -s -- \
|
||
# --app claude-desktop \
|
||
# --set HINDSIGHT_API_LLM_API_KEY=sk-...
|
||
#
|
||
# # With Ollama (local LLM)
|
||
# curl -fsSL https://hindsight.vectorize.io/get-mcp | bash -s -- \
|
||
# --app claude-desktop \
|
||
# --set HINDSIGHT_API_LLM_PROVIDER=ollama \
|
||
# --set HINDSIGHT_API_LLM_MODEL=llama3.2
|
||
#
|
||
# # With custom memory instructions
|
||
# curl -fsSL https://hindsight.vectorize.io/get-mcp | bash -s -- \
|
||
# --app claude-desktop \
|
||
# --set HINDSIGHT_API_LLM_API_KEY=sk-... \
|
||
# --set HINDSIGHT_API_MCP_INSTRUCTIONS="Also store every action you take and code you write."
|
||
#
|
||
|
||
set -e
|
||
|
||
# Colors for output
|
||
RED='\033[0;31m'
|
||
GREEN='\033[0;32m'
|
||
YELLOW='\033[1;33m'
|
||
BLUE='\033[0;34m'
|
||
NC='\033[0m' # No Color
|
||
|
||
print_info() {
|
||
echo -e "${BLUE}ℹ${NC} $1"
|
||
}
|
||
|
||
print_success() {
|
||
echo -e "${GREEN}✓${NC} $1"
|
||
}
|
||
|
||
print_error() {
|
||
echo -e "${RED}✗${NC} $1"
|
||
exit 1
|
||
}
|
||
|
||
print_warning() {
|
||
echo -e "${YELLOW}⚠${NC} $1"
|
||
}
|
||
|
||
print_banner() {
|
||
echo ""
|
||
# ANSI logo
|
||
echo -e " \033[38;2;9;127;184m▄\033[0m\033[48;2;8;130;178m\033[38;2;5;133;186m▄\033[0m \033[48;2;10;143;160m\033[38;2;10;143;165m▄\033[0m\033[38;2;7;140;156m▄\033[0m "
|
||
echo -e " \033[38;2;8;125;192m▄\033[0m \033[38;2;3;132;191m▀\033[0m\033[38;2;2;133;192m▄\033[0m \033[38;2;3;132;180m▄\033[0m\033[38;2;1;137;184m▄\033[0m\033[38;2;3;133;174m▄\033[0m \033[38;2;3;142;176m▄\033[0m\033[38;2;4;142;169m▀\033[0m \033[38;2;10;144;164m▄\033[0m "
|
||
echo -e "\033[38;2;6;121;195m▀\033[0m\033[38;2;5;128;203m▀\033[0m\033[48;2;5;124;195m\033[38;2;3;125;200m▄\033[0m\033[38;2;2;126;196m▄\033[0m\033[48;2;3;128;188m\033[38;2;1;131;196m▄\033[0m\033[48;2;0;152;219m\033[38;2;2;131;191m▄\033[0m\033[38;2;1;141;196m▀\033[0m\033[38;2;1;135;183m▀\033[0m\033[38;2;1;148;198m▀\033[0m\033[48;2;1;156;202m\033[38;2;2;135;180m▄\033[0m\033[48;2;4;134;169m\033[38;2;1;137;177m▄\033[0m\033[38;2;3;138;173m▄\033[0m\033[48;2;6;137;165m\033[38;2;2;140;170m▄\033[0m\033[38;2;7;144;169m▀\033[0m\033[38;2;7;139;158m▀\033[0m"
|
||
echo -e " \033[48;2;2;128;202m\033[38;2;2;124;201m▄\033[0m\033[48;2;1;130;201m\033[38;2;0;135;212m▄\033[0m\033[38;2;2;128;196m▄\033[0m \033[48;2;2;142;204m\033[38;2;7;138;199m▄\033[0m \033[38;2;1;135;186m▄\033[0m\033[48;2;1;142;186m\033[38;2;2;144;194m▄\033[0m\033[48;2;3;138;176m\033[38;2;2;134;176m▄\033[0m "
|
||
echo -e " \033[48;2;8;118;200m\033[38;2;8;121;209m▄\033[0m\033[38;2;3;121;203m▀\033[0m \033[38;2;3;122;192m▀\033[0m\033[38;2;1;138;216m▀\033[0m\033[48;2;0;138;210m\033[38;2;3;128;198m▄\033[0m\033[48;2;0;126;188m\033[38;2;2;131;198m▄\033[0m\033[48;2;0;142;205m\033[38;2;3;132;193m▄\033[0m\033[38;2;1;140;196m▀\033[0m \033[38;2;4;134;175m▀\033[0m\033[48;2;13;135;167m\033[38;2;8;136;174m▄\033[0m "
|
||
echo ""
|
||
echo -e " ${BLUE}HINDSIGHT MCP INSTALLER${NC}"
|
||
echo ""
|
||
}
|
||
|
||
# Parse arguments
|
||
APP=""
|
||
declare -a ENV_VARS=()
|
||
|
||
while [[ $# -gt 0 ]]; do
|
||
case $1 in
|
||
--app)
|
||
APP="$2"
|
||
shift 2
|
||
;;
|
||
--set)
|
||
ENV_VARS+=("$2")
|
||
shift 2
|
||
;;
|
||
-h|--help)
|
||
echo "Usage: $0 --app <app> --set ENV=VALUE [--set ENV2=VALUE2 ...]"
|
||
echo ""
|
||
echo "Options:"
|
||
echo " --app Required. Target application (currently only: claude-desktop)"
|
||
echo " --set ENV=VALUE Set environment variable (can be repeated)"
|
||
echo ""
|
||
echo "Examples:"
|
||
echo " # With OpenAI"
|
||
echo " $0 --app claude-desktop --set HINDSIGHT_API_LLM_API_KEY=sk-..."
|
||
echo ""
|
||
echo " # With Ollama (local LLM, no API key needed)"
|
||
echo " $0 --app claude-desktop --set HINDSIGHT_API_LLM_PROVIDER=ollama --set HINDSIGHT_API_LLM_MODEL=llama3.2"
|
||
exit 0
|
||
;;
|
||
*)
|
||
print_error "Unknown option: $1. Use --help for usage."
|
||
;;
|
||
esac
|
||
done
|
||
|
||
# Validate required arguments
|
||
if [ -z "$APP" ]; then
|
||
print_error "Missing required argument: --app. Use --help for usage."
|
||
fi
|
||
|
||
if [ "$APP" != "claude-desktop" ]; then
|
||
print_error "Unsupported app: $APP. Currently only 'claude-desktop' is supported."
|
||
fi
|
||
|
||
# Detect OS
|
||
detect_os() {
|
||
case "$(uname -s)" in
|
||
Darwin*) echo "macos" ;;
|
||
Linux*) echo "linux" ;;
|
||
MINGW*|MSYS*|CYGWIN*) echo "windows" ;;
|
||
*) echo "unknown" ;;
|
||
esac
|
||
}
|
||
|
||
OS=$(detect_os)
|
||
|
||
# Get Claude Desktop config path based on OS
|
||
get_claude_config_path() {
|
||
case "$OS" in
|
||
macos)
|
||
echo "$HOME/Library/Application Support/Claude/claude_desktop_config.json"
|
||
;;
|
||
linux)
|
||
echo "$HOME/.config/Claude/claude_desktop_config.json"
|
||
;;
|
||
windows)
|
||
echo "$APPDATA/Claude/claude_desktop_config.json"
|
||
;;
|
||
*)
|
||
print_error "Unsupported operating system: $OS"
|
||
;;
|
||
esac
|
||
}
|
||
|
||
# Check if uvx is installed and return its path
|
||
find_uvx() {
|
||
# Check if in PATH
|
||
if command -v uvx &> /dev/null; then
|
||
command -v uvx
|
||
return 0
|
||
fi
|
||
|
||
# Check common installation paths
|
||
local paths=(
|
||
"$HOME/.local/bin/uvx"
|
||
"$HOME/.cargo/bin/uvx"
|
||
"/usr/local/bin/uvx"
|
||
)
|
||
|
||
for path in "${paths[@]}"; do
|
||
if [ -f "$path" ]; then
|
||
echo "$path"
|
||
return 0
|
||
fi
|
||
done
|
||
|
||
return 1
|
||
}
|
||
|
||
# Install uv (which includes uvx)
|
||
install_uv() {
|
||
print_info "Installing uv..."
|
||
|
||
if [ "$OS" = "windows" ]; then
|
||
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
|
||
else
|
||
curl -LsSf https://astral.sh/uv/install.sh | sh
|
||
fi
|
||
|
||
# Source the env to get uvx in path
|
||
if [ -f "$HOME/.local/bin/env" ]; then
|
||
source "$HOME/.local/bin/env"
|
||
fi
|
||
|
||
# Find uvx again
|
||
if ! UVX_PATH=$(find_uvx); then
|
||
print_error "uv installed but uvx not found. Please check your installation."
|
||
fi
|
||
|
||
print_success "uv installed successfully"
|
||
}
|
||
|
||
# Update Claude Desktop config
|
||
update_claude_config() {
|
||
local config_path="$1"
|
||
local uvx_path="$2"
|
||
shift 2
|
||
local env_vars=("$@")
|
||
|
||
# Create config directory if it doesn't exist
|
||
mkdir -p "$(dirname "$config_path")"
|
||
|
||
# Check if jq is available (needed for both new and existing configs)
|
||
if ! command -v jq &> /dev/null; then
|
||
print_warning "jq not found. Installing..."
|
||
if [ "$OS" = "macos" ]; then
|
||
if command -v brew &> /dev/null; then
|
||
brew install jq
|
||
else
|
||
print_error "Please install jq: brew install jq"
|
||
fi
|
||
elif [ "$OS" = "linux" ]; then
|
||
if command -v apt-get &> /dev/null; then
|
||
sudo apt-get install -y jq
|
||
elif command -v yum &> /dev/null; then
|
||
sudo yum install -y jq
|
||
else
|
||
print_error "Please install jq manually"
|
||
fi
|
||
fi
|
||
fi
|
||
|
||
# Build the env object from env_vars array
|
||
local env_json="{}"
|
||
for env_var in "${env_vars[@]}"; do
|
||
local key="${env_var%%=*}"
|
||
local value="${env_var#*=}"
|
||
env_json=$(echo "$env_json" | jq --arg k "$key" --arg v "$value" '. + {($k): $v}')
|
||
done
|
||
|
||
# Build the hindsight server config
|
||
local hindsight_config
|
||
hindsight_config=$(jq -n \
|
||
--arg uvx "$uvx_path" \
|
||
--argjson env "$env_json" \
|
||
'{
|
||
"command": $uvx,
|
||
"args": ["--from", "hindsight-api", "hindsight-local-mcp"],
|
||
"env": $env
|
||
}')
|
||
|
||
# Check if config file exists and has content
|
||
if [ -f "$config_path" ] && [ -s "$config_path" ]; then
|
||
print_info "Updating existing Claude Desktop config..."
|
||
|
||
# Backup existing config
|
||
cp "$config_path" "${config_path}.backup"
|
||
print_info "Backed up existing config to ${config_path}.backup"
|
||
|
||
# Add or update hindsight server in existing config
|
||
local new_config
|
||
new_config=$(jq --argjson hs "$hindsight_config" '.mcpServers.hindsight = $hs' "$config_path")
|
||
|
||
echo "$new_config" > "$config_path"
|
||
else
|
||
print_info "Creating new Claude Desktop config..."
|
||
|
||
# Create new config with hindsight server
|
||
local new_config
|
||
new_config=$(jq -n --argjson hs "$hindsight_config" '{"mcpServers": {"hindsight": $hs}}')
|
||
echo "$new_config" > "$config_path"
|
||
fi
|
||
|
||
print_success "Claude Desktop config updated: $config_path"
|
||
}
|
||
|
||
# Main installation flow
|
||
main() {
|
||
print_banner
|
||
|
||
print_info "App: $APP"
|
||
if [ ${#ENV_VARS[@]} -gt 0 ]; then
|
||
print_info "Environment variables: ${#ENV_VARS[@]} configured"
|
||
fi
|
||
echo ""
|
||
|
||
# Step 1: Check/Install uvx
|
||
print_info "Checking for uvx..."
|
||
if UVX_PATH=$(find_uvx); then
|
||
print_success "uvx found at: $UVX_PATH"
|
||
else
|
||
print_warning "uvx not found. Installing uv..."
|
||
install_uv
|
||
UVX_PATH=$(find_uvx)
|
||
fi
|
||
|
||
# Step 2: Update Claude Desktop config
|
||
CONFIG_PATH=$(get_claude_config_path)
|
||
print_info "Configuring Claude Desktop..."
|
||
update_claude_config "$CONFIG_PATH" "$UVX_PATH" "${ENV_VARS[@]}"
|
||
|
||
# Done!
|
||
echo ""
|
||
print_success "Installation complete!"
|
||
echo ""
|
||
print_info "Next steps:"
|
||
echo " 1. Restart Claude Desktop"
|
||
echo " 2. Look for the 'hindsight' tools (retain, recall) in Claude"
|
||
echo ""
|
||
}
|
||
|
||
main "$@"
|