Skip to main content
Glama
install.sh26.8 kB
#!/usr/bin/env bash set -euo pipefail # MCP Agent Mail — TL;DR installer # - Installs uv (if missing) # - Sets up Python 3.14 venv with uv # - Syncs dependencies # - Runs auto-detect integration and starts the HTTP server on port 8765 # # Usage examples: # ./scripts/install.sh --yes # ./scripts/install.sh --dir "$HOME/mcp_agent_mail" --yes # curl -fsSL https://raw.githubusercontent.com/Dicklesworthstone/mcp_agent_mail/main/scripts/install.sh | bash -s -- --yes REPO_URL="https://github.com/Dicklesworthstone/mcp_agent_mail" REPO_NAME="mcp_agent_mail" BRANCH="main" DEFAULT_CLONE_DIR="$PWD/${REPO_NAME}" CLONE_DIR="" YES=0 NO_START=0 START_ONLY=0 PROJECT_DIR="" INTEGRATION_TOKEN="${INTEGRATION_BEARER_TOKEN:-}" HTTP_PORT_OVERRIDE="" SKIP_BEADS=0 SKIP_BV=0 BEADS_INSTALL_URL="https://raw.githubusercontent.com/steveyegge/beads/main/scripts/install.sh" BV_INSTALL_URL="https://raw.githubusercontent.com/Dicklesworthstone/beads_viewer/main/install.sh" SUMMARY_LINES=() LAST_BD_VERSION="" LAST_BV_VERSION="" usage() { cat <<EOF MCP Agent Mail installer Options: --dir DIR Clone/use repo at DIR (default: ./mcp_agent_mail) --branch NAME Git branch to clone (default: main) --port PORT HTTP server port (default: 8765); sets HTTP_PORT in .env --skip-beads Do not install the Beads (bd) CLI automatically --skip-bv Do not install the Beads Viewer (bv) TUI automatically -y, --yes Non-interactive; assume Yes where applicable --no-start Do not run integration/start; just set up venv + deps --start-only Skip clone/setup; run integration/start in current repo --project-dir PATH Pass-through to integration (where to write client configs) --token HEX Use/set INTEGRATION_BEARER_TOKEN for this run -h, --help Show help Examples: ./scripts/install.sh --yes ./scripts/install.sh --port 9000 --yes ./scripts/install.sh --dir "\$HOME/mcp_agent_mail" --yes curl -fsSL https://raw.githubusercontent.com/Dicklesworthstone/mcp_agent_mail/main/scripts/install.sh | bash -s -- --yes curl -fsSL https://raw.githubusercontent.com/Dicklesworthstone/mcp_agent_mail/main/scripts/install.sh | bash -s -- --port 9000 --yes EOF } while [[ $# -gt 0 ]]; do case "$1" in --dir) shift; CLONE_DIR="${1:-}" ;; --dir=*) CLONE_DIR="${1#*=}" ;; --branch) shift; BRANCH="${1:-}" ;; --branch=*) BRANCH="${1#*=}" ;; --port) shift; HTTP_PORT_OVERRIDE="${1:-}" ;; --port=*) HTTP_PORT_OVERRIDE="${1#*=}" ;; -y|--yes) YES=1 ;; --no-start) NO_START=1 ;; --start-only) START_ONLY=1 ;; --project-dir) shift; PROJECT_DIR="${1:-}" ;; --project-dir=*) PROJECT_DIR="${1#*=}" ;; --token) shift; INTEGRATION_TOKEN="${1:-}" ;; --token=*) INTEGRATION_TOKEN="${1#*=}" ;; --skip-beads) SKIP_BEADS=1 ;; --skip-bv) SKIP_BV=1 ;; -h|--help) usage; exit 0 ;; *) echo "Unknown option: $1" >&2; usage; exit 2 ;; esac shift || true done # Define logging helpers early so they're available for validation info() { printf "\033[1;36m[INFO]\033[0m %s\n" "$*"; } ok() { printf "\033[1;32m[ OK ]\033[0m %s\n" "$*"; } warn() { printf "\033[1;33m[WARN]\033[0m %s\n" "$*"; } err() { printf "\033[1;31m[ERR ]\033[0m %s\n" "$*"; } # Validate port if provided if [[ -n "${HTTP_PORT_OVERRIDE}" ]]; then if ! [[ "${HTTP_PORT_OVERRIDE}" =~ ^[0-9]+$ ]]; then err "Port must be a number (got: ${HTTP_PORT_OVERRIDE})" exit 1 fi if [[ "${HTTP_PORT_OVERRIDE}" -lt 1 || "${HTTP_PORT_OVERRIDE}" -gt 65535 ]]; then err "Port must be between 1 and 65535 (got: ${HTTP_PORT_OVERRIDE})" exit 1 fi fi need_cmd() { command -v "$1" >/dev/null 2>&1 || return 1; } record_summary() { SUMMARY_LINES+=("$1") } print_summary() { if [[ ${#SUMMARY_LINES[@]} -eq 0 ]]; then return 0 fi echo info "Installation summary" local line for line in "${SUMMARY_LINES[@]}"; do echo " - ${line}" done } find_bd_binary() { if command -v bd >/dev/null 2>&1; then command -v bd return 0 fi local candidates=("${HOME}/.local/bin/bd" "${HOME}/bin/bd" "/usr/local/bin/bd") local candidate for candidate in "${candidates[@]}"; do if [[ -x "${candidate}" ]]; then printf '%s\n' "${candidate}" return 0 fi done return 1 } maybe_add_bd_path() { local binary_path="$1" local dir dir=$(dirname "${binary_path}") if [[ ":${PATH}:" != *":${dir}:"* ]]; then export PATH="${dir}:${PATH}" ok "Temporarily added ${dir} to PATH so this session can invoke 'bd' immediately" fi } rewrite_path_snippet() { local rc_file="$1" local marker="$2" local end_marker="$3" local snippet="$4" local tmp tmp=$(mktemp "${rc_file}.XXXXXX") || return 1 local in_block=0 local replaced=0 local line while IFS='' read -r line || [[ -n "${line}" ]]; do if [[ "${in_block}" -eq 0 && "${line}" == "${marker}" ]]; then printf '%s' "${snippet}" >> "${tmp}" in_block=1 replaced=1 continue fi if [[ "${in_block}" -eq 1 ]]; then if [[ "${line}" == "${end_marker}" ]]; then in_block=0 fi continue fi printf '%s\n' "${line}" >> "${tmp}" done < "${rc_file}" if [[ "${in_block}" -eq 1 ]]; then rm -f "${tmp}" return 1 fi if [[ "${replaced}" -eq 0 ]]; then rm -f "${tmp}" return 1 fi if ! mv "${tmp}" "${rc_file}"; then rm -f "${tmp}" return 1 fi return 0 } append_path_snippet() { local dir="$1" local rc_file="$2" if [[ -z "${rc_file}" ]]; then return 1 fi local marker="# >>> MCP Agent Mail bd path ${dir}" local end_marker="# <<< MCP Agent Mail bd path" local snippet="" printf -v snippet '%s\nif [[ ":$PATH:" != *":%s:"* ]]; then\n export PATH="%s:$PATH"\nfi\n%s\n' \ "${marker}" "${dir}" "${dir}" "${end_marker}" if [[ -f "${rc_file}" ]] && grep -Fq "${marker}" "${rc_file}"; then if rewrite_path_snippet "${rc_file}" "${marker}" "${end_marker}" "${snippet}"; then ok "Updated ${dir} PATH snippet via ${rc_file}" return 0 fi warn "Existing PATH snippet in ${rc_file} could not be updated automatically" return 1 fi if ! touch "${rc_file}" >/dev/null 2>&1; then return 1 fi { printf '\n%s' "${snippet}" } >> "${rc_file}" ok "Added ${dir} to PATH via ${rc_file}" return 0 } persist_bd_path() { local binary_path="$1" local dir dir=$(dirname "${binary_path}") local shell_name="" if [[ -n "${SHELL:-}" ]]; then shell_name=$(basename "${SHELL}") fi local -a rc_candidates=() if [[ "${shell_name}" == "zsh" ]]; then rc_candidates+=("~/.zshrc") elif [[ "${shell_name}" == "bash" ]]; then rc_candidates+=("~/.bashrc") fi rc_candidates+=("~/.bashrc" "~/.zshrc" "~/.profile") local appended=0 declare -A seen_rc=() local rc for rc in "${rc_candidates[@]}"; do [[ -n "${rc}" ]] || continue local rc_path rc_path="${rc/#~/${HOME}}" if [[ -z "${rc_path}" ]]; then continue fi if [[ -n "${seen_rc["${rc_path}"]:-}" ]]; then continue fi seen_rc["${rc_path}"]=1 if append_path_snippet "${dir}" "${rc_path}"; then appended=1 break fi done if [[ "${appended}" -eq 0 ]]; then warn "Could not persist PATH update automatically; ensure ${dir} is in your PATH." fi } ensure_bd_path_ready() { local binary_path="$1" maybe_add_bd_path "${binary_path}" persist_bd_path "${binary_path}" } install_am_alias() { # Install 'am' alias to quickly start the MCP Agent Mail server local repo_dir="$1" local shell_name="" if [[ -n "${SHELL:-}" ]]; then shell_name=$(basename "${SHELL}") fi # Determine target RC file based on shell local rc_file="" if [[ "${shell_name}" == "zsh" ]]; then rc_file="${HOME}/.zshrc" elif [[ "${shell_name}" == "bash" ]]; then rc_file="${HOME}/.bashrc" else # Fallback: try zshrc first (common on macOS), then bashrc if [[ -f "${HOME}/.zshrc" ]]; then rc_file="${HOME}/.zshrc" elif [[ -f "${HOME}/.bashrc" ]]; then rc_file="${HOME}/.bashrc" else warn "Could not determine shell RC file for 'am' alias" return 1 fi fi local marker="# >>> MCP Agent Mail alias" local end_marker="# <<< MCP Agent Mail alias" local alias_cmd="alias am='cd \"${repo_dir}\" && scripts/run_server_with_token.sh'" local snippet="" printf -v snippet '%s\n%s\n%s\n' "${marker}" "${alias_cmd}" "${end_marker}" # Check if marker already exists if [[ -f "${rc_file}" ]] && grep -Fq "${marker}" "${rc_file}"; then # Update existing snippet if rewrite_path_snippet "${rc_file}" "${marker}" "${end_marker}" "${snippet}"; then ok "Updated 'am' alias in ${rc_file}" record_summary "Alias 'am': updated in ${rc_file}" return 0 fi warn "Existing 'am' alias in ${rc_file} could not be updated automatically" return 1 fi # Check if user has a different 'am' alias already if [[ -f "${rc_file}" ]] && grep -q "^alias am=" "${rc_file}"; then warn "An existing 'am' alias was found in ${rc_file}; skipping to avoid conflict" record_summary "Alias 'am': skipped (existing alias found)" return 0 fi # Append new snippet if ! touch "${rc_file}" >/dev/null 2>&1; then warn "Could not write to ${rc_file}" return 1 fi { printf '\n%s' "${snippet}" } >> "${rc_file}" ok "Added 'am' alias to ${rc_file} (run 'am' to start the server)" record_summary "Alias 'am': added to ${rc_file}" # Also define it for the current session # shellcheck disable=SC2139 alias am="cd \"${repo_dir}\" && scripts/run_server_with_token.sh" 2>/dev/null || true return 0 } verify_bd_binary() { local binary_path="$1" if ! "${binary_path}" version >/dev/null 2>&1; then err "Beads CLI at ${binary_path} failed 'bd version'. You can retry or rerun the installer with --skip-beads to handle it yourself." return 1 fi local version_line version_line=$("${binary_path}" version 2>/dev/null | head -n 1 || true) if [[ -z "${version_line}" ]]; then version_line="bd version command succeeded" fi LAST_BD_VERSION="${version_line}" ok "Beads CLI ready (${version_line})" } find_bv_binary() { if command -v bv >/dev/null 2>&1; then command -v bv return 0 fi local candidates=("${HOME}/.local/bin/bv" "${HOME}/bin/bv" "/usr/local/bin/bv") local candidate for candidate in "${candidates[@]}"; do if [[ -x "${candidate}" ]]; then printf '%s\n' "${candidate}" return 0 fi done return 1 } maybe_add_bv_path() { local binary_path="$1" local dir dir=$(dirname "${binary_path}") if [[ ":${PATH}:" != *":${dir}:"* ]]; then export PATH="${dir}:${PATH}" ok "Temporarily added ${dir} to PATH so this session can invoke 'bv' immediately" fi } verify_bv_binary() { local binary_path="$1" if ! "${binary_path}" --version >/dev/null 2>&1; then warn "Beads Viewer at ${binary_path} failed 'bv --version'" return 1 fi local version_line version_line=$("${binary_path}" --version 2>/dev/null | head -n 1 || true) if [[ -z "${version_line}" ]]; then version_line="bv --version command succeeded" fi LAST_BV_VERSION="${version_line}" ok "Beads Viewer ready (${version_line})" return 0 } ensure_uv() { if need_cmd uv; then ok "uv is already installed" record_summary "uv: already installed" return 0 fi info "Installing uv (Astral)" if ! need_cmd curl; then err "curl is required to install uv"; exit 1; fi curl -LsSf https://astral.sh/uv/install.sh | sh export PATH="${HOME}/.local/bin:${PATH}" if need_cmd uv; then ok "uv installed"; record_summary "uv: installed"; else err "uv install failed"; exit 1; fi } update_existing_repo() { # Pull latest changes from origin for an existing repo # This ensures users get bug fixes and updates when re-running the installer local repo_path="$1" info "Pulling latest changes from origin/${BRANCH}" if ! (cd "${repo_path}" && git fetch origin "${BRANCH}" --depth 1 2>/dev/null); then warn "Could not fetch from origin; continuing with existing code" return 0 fi # Check if there are updates local local_sha remote_sha local_sha=$(cd "${repo_path}" && git rev-parse HEAD 2>/dev/null || echo "unknown") remote_sha=$(cd "${repo_path}" && git rev-parse "origin/${BRANCH}" 2>/dev/null || echo "unknown") if [[ "${local_sha}" == "${remote_sha}" ]]; then ok "Already up to date (${local_sha:0:8})" record_summary "Repo: already up to date" return 0 fi # Stash any local changes to avoid conflicts local has_changes=0 if (cd "${repo_path}" && git diff --quiet 2>/dev/null) && (cd "${repo_path}" && git diff --cached --quiet 2>/dev/null); then has_changes=0 else has_changes=1 info "Stashing local changes before update" (cd "${repo_path}" && git stash push -m "installer-auto-stash-$(date +%Y%m%d%H%M%S)" 2>/dev/null) || true fi # Reset to origin/BRANCH to get latest code if (cd "${repo_path}" && git reset --hard "origin/${BRANCH}" 2>/dev/null); then ok "Updated to latest (${local_sha:0:8} → ${remote_sha:0:8})" record_summary "Repo: updated ${local_sha:0:8} → ${remote_sha:0:8}" else warn "Could not update repo; continuing with existing code" record_summary "Repo: update failed, using existing" fi # Restore stashed changes if any if [[ "${has_changes}" -eq 1 ]]; then if (cd "${repo_path}" && git stash pop 2>/dev/null); then ok "Restored local changes" else warn "Could not restore stashed changes; they're saved in git stash" fi fi } ensure_repo() { # Determine target directory if [[ -z "${CLONE_DIR}" ]]; then CLONE_DIR="${DEFAULT_CLONE_DIR}"; fi # If we're already in the repo (local run), use it and update if [[ -f "pyproject.toml" ]] && grep -q '^name\s*=\s*"mcp-agent-mail"' pyproject.toml 2>/dev/null; then REPO_DIR="$PWD" ok "Using existing repo at: ${REPO_DIR}" update_existing_repo "${REPO_DIR}" return 0 fi # If directory exists and looks like the repo, use it and update if [[ -d "${CLONE_DIR}" ]] && [[ -f "${CLONE_DIR}/pyproject.toml" ]] && grep -q '^name\s*=\s*"mcp-agent-mail"' "${CLONE_DIR}/pyproject.toml" 2>/dev/null; then REPO_DIR="${CLONE_DIR}" ok "Using existing repo at: ${REPO_DIR}" update_existing_repo "${REPO_DIR}" return 0 fi # Otherwise clone info "Cloning ${REPO_URL} (branch=${BRANCH}) to ${CLONE_DIR}" need_cmd git || { err "git is required to clone"; exit 1; } git clone --depth 1 --branch "${BRANCH}" "${REPO_URL}" "${CLONE_DIR}" REPO_DIR="${CLONE_DIR}" ok "Cloned repo" record_summary "Repo: cloned into ${REPO_DIR}" } ensure_python_and_venv() { info "Ensuring Python 3.14 and project venv (.venv)" uv python install 3.14 if [[ ! -d "${REPO_DIR}/.venv" ]]; then (cd "${REPO_DIR}" && uv venv -p 3.14) ok "Created venv at ${REPO_DIR}/.venv" record_summary "Venv: created at ${REPO_DIR}/.venv" else ok "Found existing venv at ${REPO_DIR}/.venv" record_summary "Venv: existing at ${REPO_DIR}/.venv" fi } sync_deps() { info "Syncing dependencies with uv" ( cd "${REPO_DIR}" # shellcheck disable=SC1091 source .venv/bin/activate uv sync ) ok "Dependencies installed" record_summary "Dependencies: uv sync complete" } configure_port() { if [[ -z "${HTTP_PORT_OVERRIDE}" ]]; then return 0 fi local env_file="${REPO_DIR}/.env" local tmp="${env_file}.tmp.$$" info "Configuring HTTP_PORT=${HTTP_PORT_OVERRIDE} in .env" # Set trap to cleanup temp file trap "rm -f \"${tmp}\" 2>/dev/null" EXIT INT TERM # Set secure umask for .env file operations, save original to restore later local old_umask old_umask=$(umask) umask 077 if [[ -f "${env_file}" ]]; then # File exists - update or append if grep -q '^HTTP_PORT=' "${env_file}"; then # Replace existing value if ! sed "s/^HTTP_PORT=.*/HTTP_PORT=${HTTP_PORT_OVERRIDE}/" "${env_file}" > "${tmp}"; then err "Failed to update HTTP_PORT in .env" rm -f "${tmp}" 2>/dev/null trap - EXIT INT TERM umask "${old_umask}" return 1 fi else # Append new value if ! { cat "${env_file}"; echo "HTTP_PORT=${HTTP_PORT_OVERRIDE}"; } > "${tmp}"; then err "Failed to append HTTP_PORT to .env" rm -f "${tmp}" 2>/dev/null trap - EXIT INT TERM umask "${old_umask}" return 1 fi fi # Atomic move if ! mv "${tmp}" "${env_file}"; then err "Failed to write .env file" rm -f "${tmp}" 2>/dev/null trap - EXIT INT TERM umask "${old_umask}" return 1 fi else # Create new file if ! echo "HTTP_PORT=${HTTP_PORT_OVERRIDE}" > "${tmp}"; then err "Failed to create .env file" rm -f "${tmp}" 2>/dev/null trap - EXIT INT TERM umask "${old_umask}" return 1 fi if ! mv "${tmp}" "${env_file}"; then err "Failed to write .env file" rm -f "${tmp}" 2>/dev/null trap - EXIT INT TERM umask "${old_umask}" return 1 fi fi # Ensure secure permissions (in case file existed with wrong perms) chmod 600 "${env_file}" 2>/dev/null || warn "Could not set .env permissions to 600" trap - EXIT INT TERM umask "${old_umask}" ok "HTTP_PORT set to ${HTTP_PORT_OVERRIDE}" record_summary "HTTP port: ${HTTP_PORT_OVERRIDE}" } run_integration_and_start() { if [[ "${NO_START}" -eq 1 ]]; then warn "--no-start specified; skipping integration/start" return 0 fi info "Running auto-detect integration and starting server" ( cd "${REPO_DIR}" # shellcheck disable=SC1091 source .venv/bin/activate export INTEGRATION_BEARER_TOKEN="${INTEGRATION_TOKEN}" args=() if [[ "${YES}" -eq 1 ]]; then args+=("--yes"); fi if [[ -n "${PROJECT_DIR}" ]]; then args+=("--project-dir" "${PROJECT_DIR}"); fi bash scripts/automatically_detect_all_installed_coding_agents_and_install_mcp_agent_mail_in_all.sh "${args[@]}" ) } ensure_beads() { if [[ "${SKIP_BEADS}" -eq 1 ]]; then warn "--skip-beads specified; not installing Beads CLI" record_summary "Beads CLI: skipped (--skip-beads)" return 0 fi local bd_path if bd_path=$(find_bd_binary); then verify_bd_binary "${bd_path}" || exit 1 ensure_bd_path_ready "${bd_path}" record_summary "Beads CLI: ${LAST_BD_VERSION}" return 0 fi info "Installing Beads (bd) CLI" if ! need_cmd curl; then err "curl is required to install Beads automatically" exit 1 fi if ! curl -fsSL "${BEADS_INSTALL_URL}" | bash; then err "Failed to install Beads automatically. You can install manually via: curl -fsSL ${BEADS_INSTALL_URL} | bash" exit 1 fi hash -r 2>/dev/null || true if bd_path=$(find_bd_binary); then verify_bd_binary "${bd_path}" || exit 1 ensure_bd_path_ready "${bd_path}" record_summary "Beads CLI: ${LAST_BD_VERSION}" return 0 fi err "Beads installer finished but 'bd' was not detected. Ensure its install directory is on PATH or rerun with --skip-beads to handle installation manually." exit 1 } install_cli_stub() { # Install a helpful "mcp-agent-mail" command that explains this is NOT a CLI tool # This catches agents that mistakenly try to run it as a shell command local stub_dir="${HOME}/.local/bin" local stub_path="${stub_dir}/mcp-agent-mail" mkdir -p "${stub_dir}" 2>/dev/null || true cat > "${stub_path}" <<'STUB_EOF' #!/usr/bin/env bash # MCP Agent Mail — Helpful Stub for Confused Agents # # If you're seeing this, you (or an AI agent) tried to run "mcp-agent-mail" # as a CLI command. That's a common mistake! cat <<'MSG' ╔══════════════════════════════════════════════════════════════════════════════╗ ║ ║ ║ 🚫 MCP Agent Mail is NOT a CLI tool! ║ ║ ║ ║ It's an MCP (Model Context Protocol) server that provides tools to your ║ ║ AI coding agent. You should already have access to these tools as part ║ ║ of your available MCP tools. ║ ║ ║ ║ ✅ CORRECT USAGE: ║ ║ Use the MCP tools directly, for example: ║ ║ • mcp__mcp-agent-mail__register_agent ║ ║ • mcp__mcp-agent-mail__send_message ║ ║ • mcp__mcp-agent-mail__fetch_inbox ║ ║ ║ ║ ❌ INCORRECT USAGE: ║ ║ Running shell commands like: ║ ║ • mcp-agent-mail send --to BlueLake ... ║ ║ • mcp-agent-mail --help ║ ║ ║ ║ 📚 For documentation, see: ║ ║ https://github.com/Dicklesworthstone/mcp_agent_mail ║ ║ ║ ╚══════════════════════════════════════════════════════════════════════════════╝ MSG exit 1 STUB_EOF chmod +x "${stub_path}" 2>/dev/null || true # Also create common aliases/variants agents might try for variant in "mcp_agent_mail" "mcpagentmail" "agentmail" "agent-mail"; do local variant_path="${stub_dir}/${variant}" if [[ ! -f "${variant_path}" ]]; then ln -sf "${stub_path}" "${variant_path}" 2>/dev/null || true fi done ok "Installed helpful CLI stub at ${stub_path}" record_summary "CLI stub: installed (catches mistaken CLI usage)" } ensure_bv() { if [[ "${SKIP_BV}" -eq 1 ]]; then warn "--skip-bv specified; not installing Beads Viewer" record_summary "Beads Viewer: skipped (--skip-bv)" return 0 fi local bv_path if bv_path=$(find_bv_binary); then if verify_bv_binary "${bv_path}"; then maybe_add_bv_path "${bv_path}" record_summary "Beads Viewer: ${LAST_BV_VERSION}" else warn "Beads Viewer found but verification failed; continuing without bv" record_summary "Beads Viewer: found but failed verification" fi return 0 fi info "Installing Beads Viewer (bv) TUI (optional)" if ! need_cmd curl; then warn "curl not available; skipping Beads Viewer installation" record_summary "Beads Viewer: skipped (no curl)" return 0 fi if ! curl -fsSL "${BV_INSTALL_URL}" | bash; then warn "Beads Viewer installation failed (non-fatal). You can install manually via: curl -fsSL ${BV_INSTALL_URL} | bash" record_summary "Beads Viewer: installation failed (optional)" return 0 fi hash -r 2>/dev/null || true if bv_path=$(find_bv_binary); then if verify_bv_binary "${bv_path}"; then maybe_add_bv_path "${bv_path}" record_summary "Beads Viewer: ${LAST_BV_VERSION}" else warn "Beads Viewer installed but verification failed" record_summary "Beads Viewer: installed but failed verification" fi return 0 fi warn "Beads Viewer installer finished but 'bv' was not detected. You can install manually via: curl -fsSL ${BV_INSTALL_URL} | bash" record_summary "Beads Viewer: not detected after install (optional)" return 0 } offer_doc_blurbs() { if [[ "${YES}" -eq 1 ]]; then info "Docs helper available anytime via: uv run python -m mcp_agent_mail.cli docs insert-blurbs" return 0 fi echo echo "Would you like to automatically detect your code projects and insert the relevant blurbs for Agent Mail into your AGENTS.md and CLAUDE.md files?" echo "You will be able to confirm for each detecting project if you want to do that. Otherwise, just skip that, but be sure to add the blurbs yourself manually for the system to work properly." read -r -p "[y/N] " doc_choice doc_choice=$(printf '%s' "${doc_choice}" | tr -d '[:space:]') case "${doc_choice}" in y|Y) ( cd "${REPO_DIR}" && uv run python -m mcp_agent_mail.cli docs insert-blurbs ) || warn "Docs helper encountered an issue. You can rerun it later with: uv run python -m mcp_agent_mail.cli docs insert-blurbs" ;; *) info "Skipping automatic doc updates; remember to add the blurbs manually." ;; esac } main() { if [[ "${START_ONLY}" -eq 1 ]]; then info "--start-only specified: skipping clone/setup; starting integration" REPO_DIR="$PWD" record_summary "Repo: existing at ${REPO_DIR} (--start-only)" ensure_beads ensure_bv install_cli_stub install_am_alias "${REPO_DIR}" configure_port if ! run_integration_and_start; then err "Integration failed; aborting." exit 1 fi record_summary "Integration: auto-detect + server start" print_summary offer_doc_blurbs exit 0 fi ensure_uv ensure_beads ensure_bv install_cli_stub ensure_repo ensure_python_and_venv sync_deps install_am_alias "${REPO_DIR}" configure_port if ! run_integration_and_start; then err "Integration failed; aborting." exit 1 fi record_summary "Integration: auto-detect + server start" print_summary offer_doc_blurbs echo ok "All set!" echo "Next runs (open a new terminal or run 'source ~/.zshrc' / 'source ~/.bashrc'):" echo " am # quick alias to start the server" echo " # or manually:" echo " cd \"${REPO_DIR}\"" echo " source .venv/bin/activate" echo " bash scripts/run_server_with_token.sh" } # Handle three execution modes: # 1. Direct: ./install.sh → BASH_SOURCE[0] == $0 → run main # 2. Sourced: . install.sh → BASH_SOURCE[0] != $0 → skip main # 3. Piped: curl ... | bash -s → BASH_SOURCE[0] is unset → run main if [[ -z "${BASH_SOURCE[0]:-}" ]] || [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then main "$@" fi

Latest Blog Posts

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/Dicklesworthstone/mcp_agent_mail'

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