Skip to main content
Glama

Karakeep MCP server

by karakeep-app
karakeep-linux.sh17.4 kB
#!/usr/bin/env bash # v3.0.0 # Copyright 2024-2025 # Author: vhsdream # Adapted from: The Karakeep installation script from https://github.com/community-scripts/ProxmoxVE # License: MIT set -Eeuo pipefail trap 'catch $LINENO "$BASH_COMMAND"' SIGINT SIGTERM ERR verbose=0 SPINNER_PID="" usage() { header cat <<EOF This script has three functions: 'install' Installs $(app) on a clean Debian 12/Ubuntu 24.04 system. 'update' Checks for, and installs updates for $(app) on a system that previously installed $(app) by running this script. 'migrate' Performs a full Hoarder ==> $(app) migration on a previous install. Also checks for updates afterwards. Available options: -h, --help Print this help and exit -v, --verbose Print script StandardOutput --no-color Disable colours This script WILL NOT update or migrate a $(app)/Hoarder install that was installed in any other way. Please back up your existing installation before running this script! If you encounter any errors please create a GitHub issue (https://github.com/karakeep-app/karakeep/issues) and tag vhsdream Usage: bash $(basename "${BASH_SOURCE[0]}") [-h] [-v] [install|update|migrate] EOF exit } header() { t_width=$(tput cols 2>/dev/null) if [[ "$t_width" -gt 115 ]]; then echo -e "$( cat <<EOF ${YELLOW} oMMWWWWWMMMMMMMMMMMo .... ... oMM 'MMMMMMMMMMo xMMX ;MMM. oMM 'MMOxkXMMMMo kMMX ;MMM. oMM 'MM' .OMMo kMMX ,;;;. .;lll:. ';;. :l ,cloc, ;MMM. .;;;. .:loc, .:llc' ;;; .clc, oMM 'MM' .MMo kMMX .0MMX, dMMXKWMMO 0MM0NMM'WMNKXMMW' ;MMM. dMMWl 'KMW0OXMWl ;NMW0ONMN: .MMMXMWMMMWl oMM .MM' MMo kMMNlWMWl ...',NMM: 0MMWc. '.',xMMK ;MMMlNMMk. .WMW:'''KMMc;MMW,'''XMM,.MMMk. ;WMM: oMM .MM' MMo kMMWKMMX. .xWMN0ONMMo 0MMO cXMWKOKMMW ;MMM0MMW: cMMMXXXXXXXddMMWXXXXXXXc.MMM. KMMx oMM .MM'.ol. MMo kMMX xMMWl dMM0 .XMMo 0MMO .MMM. dMMW ;MMM.:WMMO .NMMo...lc. .WMWc...o: .MMMX:',xMMM' oMMXKKKKXMMWMMMMNMMo kMMX :WMM0,0MMMWX0MMo 0MMO dWMMWWxMMW ;MMM. .KMMN,.dNMMMMMNo .kWMMMMMXc .MMM0WMMMM0' .:cccccccccccccccc:. ... ... ..'.. .''. .MMM, ... .MMM, .OOO.${CLR} The ${PURPLE}Bookmark${CLR} ${RED}Everything${CLR} ${YELLOW}App${CLR} script version ${GREEN}3.0.0${CLR} EOF )" fi } # Handling output suppression set_verbosity() { if [ "$verbose" -eq 1 ]; then shh="" else shh="silent_running" fi } silent_running() { "$@" >/dev/null 2>&1 } set_verbosity # Colour handling setup_colours() { if [[ -t 2 ]] && [[ -z "${NO_COLOR-}" ]] && [[ "${TERM-}" != "dumb" ]]; then CLR='\033[0m' GREEN='\033[0;32m' PURPLE='\033[0;35m' CYAN='\033[0;36m' YELLOW='\033[1;33m' RED='\033[0;31m' else CLR='' GREEN='' PURPLE='' CYAN='' YELLOW='' RED='' fi } setup_colours # Flash and bling app() { echo -e "${CLR}${PURPLE}Karakeep${CLR}" } spinner() { local frames=('▹▹▹▹▹' '▸▹▹▹▹' '▹▸▹▹▹' '▹▹▸▹▹' '▹▹▹▸▹' '▹▹▹▹▸') local spin_i=0 local interval=0.1 printf "\e[?25l" while true; do printf "\r${PURPLE}%s${CLR}" "${frames[spin_i]}" spin_i=$(((spin_i + 1) % ${#frames[@]})) sleep "$interval" done } msg_start() { printf " " echo >&1 -ne "${CYAN}${1-}${CLR}" spinner & SPINNER_PID=$! } msg_done() { if [[ -n "$SPINNER_PID" ]] && ps -p "$SPINNER_PID" >/dev/null; then kill "$SPINNER_PID" >/dev/null; fi printf "\e[?25h" local msg="${1-}" echo -e "\r" echo >&1 -e "${GREEN}Done ✔ ${msg}${CLR}" } msg_info() { if [[ -n "$SPINNER_PID" ]] && ps -p "$SPINNER_PID" >/dev/null; then kill "$SPINNER_PID" >/dev/null; fi printf "\e[?25h" echo >&1 -e "${1-}\r" } # Exception and error handling msg_err() { if [[ -n "$SPINNER_PID" ]] && ps -p "$SPINNER_PID" >/dev/null; then kill "$SPINNER_PID" >/dev/null; fi echo >&2 -e "${RED}${1-}${CLR}" } die() { local err=$1 local code=${2-1} msg_err "$err" exit "$code" } catch() { local code=$? local line=$1 local command=$2 msg_err "Caught error in line $line: exit code $code: while executing $command" } parse_params() { while :; do case "${1-}" in -h | --help) usage ;; -v | --verbose) verbose=1 && set_verbosity ;; --no-color) NO_COLOR=1 ;; -?*) die "Unknown flag: $1. Use -h|--help for help" ;; *) break ;; esac shift done args=("$@") [[ ${#args[@]} -eq 0 ]] && die "Missing script arguments. Use -h|--help for help" return 0 } parse_params "$@" OS="$(awk -F'=' '/^VERSION_CODENAME=/{ print $NF }' /etc/os-release)" INSTALL_DIR=/opt/karakeep APP_DIR="$INSTALL_DIR"/apps export DATA_DIR=/var/lib/karakeep CONFIG_DIR=/etc/karakeep LOG_DIR=/var/log/karakeep ENV_FILE=${CONFIG_DIR}/karakeep.env install_karakeep() { header msg_info "$(app) installation for Debian 12/Ubuntu 24.04" && sleep 3 echo -e "\n" msg_start "Installing dependencies..." $shh apt-get install --no-install-recommends -y \ g++ \ curl \ build-essential \ sudo \ unzip \ gnupg \ graphicsmagick \ ghostscript \ ca-certificates if [[ "$OS" == "noble" ]]; then $shh apt-get install -y software-properties-common $shh add-apt-repository ppa:xtradeb/apps -y $shh apt-get install --no-install-recommends -y ungoogled-chromium yt-dlp ln -s /usr/bin/ungoogled-chromium /usr/bin/chromium else $shh apt-get install --no-install-recommends -y chromium curl -fsSL https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp_linux -o /usr/bin/yt-dlp && chmod +x /usr/bin/yt-dlp fi curl -fsSL https://github.com/Y2Z/monolith/releases/latest/download/monolith-gnu-linux-x86_64 -o /usr/bin/monolith && chmod +x /usr/bin/monolith curl -fsSLO https://github.com/meilisearch/meilisearch/releases/latest/download/meilisearch.deb DEBIAN_FRONTEND=noninteractive $shh apt-get install -y ./meilisearch.deb && rm ./meilisearch.deb msg_done "Installed dependencies" msg_start "Installing Node.js..." mkdir -p /etc/apt/keyrings curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_22.x nodistro main" >/etc/apt/sources.list.d/nodesource.list $shh apt-get update $shh apt-get install -y nodejs # https://github.com/karakeep-app/karakeep/issues/967 $shh npm install -g corepack@0.31.0 msg_done "Installed Node.js" msg_start "Installing $(app)${CYAN}, please wait...${CLR}" mkdir -p {"$DATA_DIR","$CONFIG_DIR","$LOG_DIR"} M_DATA_DIR=/var/lib/meilisearch M_CONFIG_FILE=/etc/meilisearch.toml RELEASE="$(curl -s https://api.github.com/repos/karakeep-app/karakeep/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }')" cd /tmp curl -fsSLO "https://github.com/karakeep-app/karakeep/archive/refs/tags/v${RELEASE}.zip" unzip -q v"$RELEASE".zip mv karakeep-"$RELEASE" "$INSTALL_DIR" && cd "$APP_DIR"/web corepack enable export NEXT_TELEMETRY_DISABLED=1 export PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD="true" export CI="true" $shh pnpm i --frozen-lockfile $shh pnpm build cd "$APP_DIR"/workers $shh pnpm i --frozen-lockfile $shh pnpm build cd "$APP_DIR"/cli $shh pnpm i --frozen-lockfile $shh pnpm build cd "$INSTALL_DIR"/packages/db $shh pnpm migrate msg_done "Installed $(app)" msg_start "Creating configuration files..." cd "$INSTALL_DIR" MASTER_KEY="$(openssl rand -base64 12)" cat <<EOF >"$M_CONFIG_FILE" env = "production" master_key = "$MASTER_KEY" db_path = "${M_DATA_DIR}/data" dump_dir = "${M_DATA_DIR}/dumps" snapshot_dir = "${M_DATA_DIR}/snapshots" no_analytics = true EOF chmod 600 "$M_CONFIG_FILE" karakeep_SECRET="$(openssl rand -base64 36 | cut -c1-24)" cat <<EOF >"$ENV_FILE" NODE_ENV=production SERVER_VERSION=${RELEASE} NEXTAUTH_SECRET="${karakeep_SECRET}" NEXTAUTH_URL="http://localhost:3000" DATA_DIR=${DATA_DIR} MEILI_ADDR="http://127.0.0.1:7700" MEILI_MASTER_KEY="${MASTER_KEY}" BROWSER_WEB_URL="http://127.0.0.1:9222" # CRAWLER_VIDEO_DOWNLOAD=true # CRAWLER_VIDEO_DOWNLOAD_MAX_SIZE= # OPENAI_API_KEY= # OLLAMA_BASE_URL= # INFERENCE_TEXT_MODEL= # INFERENCE_IMAGE_MODEL= EOF chmod 600 "$ENV_FILE" echo "$RELEASE" >"$INSTALL_DIR"/version.txt msg_done "Configuration complete" msg_start "Creating users and modifying permissions..." useradd -U -s /usr/sbin/nologin -r -m -d "$M_DATA_DIR" meilisearch useradd -U -s /usr/sbin/nologin -r -M -d "$INSTALL_DIR" karakeep chown meilisearch:meilisearch "$M_CONFIG_FILE" touch "$LOG_DIR"/{karakeep-workers.log,karakeep-web.log} chown -R karakeep:karakeep "$INSTALL_DIR" "$CONFIG_DIR" "$DATA_DIR" "$LOG_DIR" msg_done "Users created, permissions modified" msg_start "Creating service files and configuring log rotation..." cat <<EOF >/etc/systemd/system/meilisearch.service [Unit] Description=MeiliSearch is a RESTful search API Documentation=https://docs.meilisearch.com/ After=network.target [Service] User=meilisearch Group=meilisearch Restart=on-failure WorkingDirectory=${M_DATA_DIR} ExecStart=/usr/bin/meilisearch --config-file-path ${M_CONFIG_FILE} NoNewPrivileges=true ProtectHome=true ReadWritePaths=${M_DATA_DIR} ProtectSystem=full ProtectHostname=true ProtectControlGroups=true ProtectKernelModules=true ProtectKernelTunables=true ProtectKernelLogs=true ProtectClock=true LockPersonality=true RestrictRealtime=yes RestrictNamespaces=yes MemoryDenyWriteExecute=yes PrivateDevices=yes PrivateTmp=true CapabilityBoundingSet= RemoveIPC=true [Install] WantedBy=multi-user.target EOF cat <<EOF >/etc/systemd/system/karakeep-browser.service [Unit] Description=Karakeep headless browser After=network.target [Service] User=root Restart=on-failure ExecStart=/usr/bin/chromium --headless --no-sandbox --disable-gpu --disable-dev-shm-usage --remote-debugging-address=127.0.0.1 --remote-debugging-port=9222 --hide-scrollbars TimeoutStopSec=5 SyslogIdentifier=karakeep-browser [Install] WantedBy=multi-user.target EOF cat <<EOF >/etc/systemd/system/karakeep-workers.service [Unit] Description=Karakeep workers Wants=network.target karakeep-browser.service meilisearch.service After=network.target karakeep-browser.service meilisearch.service [Service] User=karakeep Group=karakeep Restart=always EnvironmentFile=${ENV_FILE} WorkingDirectory=${APP_DIR}/workers ExecStart=/usr/bin/node dist/index.js StandardOutput=append:${LOG_DIR}/karakeep-workers.log StandardError=append:${LOG_DIR}/karakeep-workers.log TimeoutStopSec=5 SyslogIdentifier=karakeep-workers [Install] WantedBy=multi-user.target EOF cat <<EOF >/etc/systemd/system/karakeep-web.service [Unit] Description=Karakeep web Wants=network.target karakeep-workers.service After=network.target karakeep-workers.service [Service] User=karakeep Group=karakeep Restart=on-failure EnvironmentFile=${ENV_FILE} WorkingDirectory=${APP_DIR}/web ExecStart=/usr/bin/pnpm start StandardOutput=append:${LOG_DIR}/karakeep-web.log StandardError=append:${LOG_DIR}/karakeep-web.log TimeoutStopSec=5 SyslogIdentifier=karakeep-web [Install] WantedBy=multi-user.target EOF cat <<EOF >/etc/systemd/system/karakeep.target [Unit] Description=Karakeep Services After=network-online.target Wants=meilisearch.service karakeep-browser.service karakeep-workers.service karakeep-web.service [Install] WantedBy=multi-user.target EOF cat <<EOF >/etc/logrotate.d/karakeep /var/log/karakeep/*.log { su karakeep karakeep weekly missingok rotate 4 compress notifempty } EOF msg_done "Service files created, log rotation configured" msg_start "Enabling and starting services, please wait..." && sleep 3 systemctl enable -q --now meilisearch.service karakeep.target service_check install exit 0 } update_karakeep() { msg_info "${YELLOW}Checking for an update...${CLR}" && sleep 1 if [[ ! -d ${INSTALL_DIR} ]]; then die "Is Karakeep even installed?" fi RELEASE="$(curl -s https://api.github.com/repos/karakeep-app/karakeep/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }')" PREV_RELEASE="$(cat "$INSTALL_DIR"/version.txt)" if [[ "$RELEASE" != "$PREV_RELEASE" ]]; then if [[ "$(systemctl is-active karakeep-web)" == "active" ]]; then msg_start "Stopping affected services..." systemctl stop karakeep-web karakeep-workers msg_done "Stopped services" fi if [[ "$OS" == "bookworm" ]]; then $shh yt-dlp -U fi msg_start "Updating $(app) ${CYAN}to v${RELEASE}...${CLR}" sed -i "s|SERVER_VERSION=${PREV_RELEASE}|SERVER_VERSION=${RELEASE}|" "$ENV_FILE" rm -R "$INSTALL_DIR" cd /tmp curl -fsSLO "https://github.com/karakeep-app/karakeep/archive/refs/tags/v${RELEASE}.zip" unzip -q v"$RELEASE".zip mv karakeep-"$RELEASE" "$INSTALL_DIR" # https://github.com/karakeep-app/karakeep/issues/967 if [[ "$(corepack -v)" < "0.31.0" ]]; then $shh npm install -g corepack@0.31.0 fi corepack enable export NEXT_TELEMETRY_DISABLED=1 export PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD="true" export CI="true" cd "$APP_DIR"/web && $shh pnpm i --frozen-lockfile $shh pnpm build cd "$APP_DIR"/workers && $shh pnpm i --frozen-lockfile $shh pnpm build cd "$APP_DIR"/cli && $shh pnpm i --frozen-lockfile $shh pnpm build cd "$INSTALL_DIR"/packages/db && $shh pnpm migrate echo "$RELEASE" >"$INSTALL_DIR"/version.txt chown -R karakeep:karakeep "$INSTALL_DIR" "$DATA_DIR" msg_done "Updated $(app) ${CYAN}to v${RELEASE}${CLR}" msg_start "Restarting services and cleaning up..." rm /tmp/v"$RELEASE".zip # Migrations # 0.27 changed the worker compiled file from .mjs to .js if grep -q '^ExecStart=/usr/bin/node\s\+dist/index\.mjs$' /etc/systemd/system/karakeep-workers.service; then sed -i -E 's#^(ExecStart=/usr/bin/node\s+dist/)index\.mjs$#\1index.js#' /etc/systemd/system/karakeep-workers.service systemctl daemon-reload fi # End migrations systemctl restart karakeep.target service_check update else msg_info "${YELLOW}No update required.${CLR}" fi exit 0 } migrate_karakeep() { if [[ ! -d /opt/karakeep ]]; then msg_start "Migrating your Hoarder installation to $(app), ${CYAN}then checking for an update...${CLR}" systemctl stop hoarder-browser hoarder-workers hoarder-web sed -i -e "s|hoarder|karakeep|g" /etc/hoarder/hoarder.env /etc/systemd/system/hoarder-{browser,web,workers}.service /etc/systemd/system/hoarder.target \ -e "s|Hoarder|Karakeep|g" /etc/systemd/system/hoarder-{browser,web,workers}.service /etc/systemd/system/hoarder.target for path in /etc/systemd/system/hoarder*.service; do new_path="${path//hoarder/karakeep}" mv "$path" "$new_path" done mv /etc/systemd/system/hoarder.target /etc/systemd/system/karakeep.target mv /opt/hoarder "$INSTALL_DIR" mv /var/lib/hoarder "$DATA_DIR" mv /etc/hoarder "$CONFIG_DIR" mv /var/log/hoarder "$LOG_DIR" mv "$CONFIG_DIR"/hoarder.env "$ENV_FILE" mv "$LOG_DIR"/hoarder-web.log "$LOG_DIR"/karakeep-web.log mv "$LOG_DIR"/hoarder-workers.log "$LOG_DIR"/karakeep-workers.log usermod -l karakeep hoarder -d "$INSTALL_DIR" groupmod -n karakeep hoarder chown -R karakeep:karakeep "$INSTALL_DIR" "$CONFIG_DIR" "$DATA_DIR" "$LOG_DIR" systemctl daemon-reload systemctl -q enable --now karakeep.target service_check migrate else msg_info "${YELLOW}There is no need for a migration: $(app) ${YELLOW}is already installed.${CLR}" fi } service_check() { local services=("karakeep-browser" "karakeep-workers" "karakeep-web" "meilisearch") readarray -t status < <(for service in "${services[@]}"; do systemctl is-active "$service" | grep "^active" - done) if [[ "${#status[@]}" -eq 4 ]]; then if [[ "$1" == "install" ]]; then msg_done "$(app) ${CYAN}is running!${CLR}" sleep 1 LOCAL_IP="$(hostname -I | awk '{print $1}')" msg_info "Go to ${YELLOW}http://$LOCAL_IP:3000 ${CLR}to create your account" msg_info "Change settings at ${YELLOW}'/etc/karakeep/karakeep.env'${CLR}" exit 0 elif [[ "$1" == "update" ]]; then msg_done "$(app) ${CYAN}is updated and running!${CLR}" sleep 1 exit 0 elif [[ "$1" == "migrate" ]]; then msg_done "$(app) ${CYAN}migration complete!${CLR}" sleep 1 exit 0 fi else die "Some services have failed. Check 'journalctl -xeu <service-name>' to see what is going on" fi } [ "$(id -u)" -ne 0 ] && die "This script requires root privileges. Please run with sudo or as the root user." case "${args[0]}" in install) install_karakeep ;; update) update_karakeep ;; migrate) migrate_karakeep && update_karakeep ;; *) die "Unknown command. Choose 'install', 'update' or 'migrate.'" ;; esac

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/karakeep-app/karakeep'

If you have feedback or need assistance with the MCP directory API, please join our Discord server