From a4f2adc50a42dffd8dda4e4b9646b6909b11a576 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 21 Mar 2026 15:03:27 +0000 Subject: [PATCH 1/2] fix mcp-server crash and Kiwix FQDN resolution MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit mcp_server.py / local-ai-setup.sh: FastMCP.get_asgi_app() was removed in mcp 1.x — replace with sse_app(), which returns the same Starlette ASGI app for SSE transport. Fixes the crash loop the container was stuck in. laptop_full_setup.sh Caddyfile: $(hostname).local requires mDNS (Avahi/Bonjour) to resolve from a proxy machine, which is often not available on all LAN clients. Replace every occurrence with $LOCAL_IP (the machine's LAN IP) so the generated Caddyfile.example works reliably regardless of mDNS support. https://claude.ai/code/session_012gDnantBmFTWZGCiKyjazx --- laptop_full_setup.sh | 16 ++++++++-------- local-ai-setup.sh | 2 +- mcp_server.py | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/laptop_full_setup.sh b/laptop_full_setup.sh index e193db6..12f7549 100755 --- a/laptop_full_setup.sh +++ b/laptop_full_setup.sh @@ -1392,19 +1392,19 @@ write_if_new "$BASE/Caddyfile.example" << CADDY # Replace yourdomain.com with your actual domain webui.yourdomain.com { - reverse_proxy $(hostname).local:3000 { + reverse_proxy $LOCAL_IP:3000 { header_up X-Forwarded-Proto {scheme} header_up X-Forwarded-Host {host} } } -invokeai.yourdomain.com { reverse_proxy $(hostname).local:9090 } -search.yourdomain.com { reverse_proxy $(hostname).local:8888 } -git.yourdomain.com { reverse_proxy $(hostname).local:3001 } -kiwix.yourdomain.com { reverse_proxy $(hostname).local:8181 } -rag.yourdomain.com { reverse_proxy $(hostname).local:8001 } -mcp.yourdomain.com { reverse_proxy $(hostname).local:8002 } +invokeai.yourdomain.com { reverse_proxy $LOCAL_IP:9090 } +search.yourdomain.com { reverse_proxy $LOCAL_IP:8888 } +git.yourdomain.com { reverse_proxy $LOCAL_IP:3001 } +kiwix.yourdomain.com { reverse_proxy $LOCAL_IP:8181 } +rag.yourdomain.com { reverse_proxy $LOCAL_IP:8001 } +mcp.yourdomain.com { reverse_proxy $LOCAL_IP:8002 } portainer.yourdomain.com { - reverse_proxy https://$(hostname).local:9443 { + reverse_proxy https://$LOCAL_IP:9443 { transport http { tls_insecure_skip_verify } } } diff --git a/local-ai-setup.sh b/local-ai-setup.sh index b1d8531..cb3a202 100755 --- a/local-ai-setup.sh +++ b/local-ai-setup.sh @@ -669,7 +669,7 @@ def rag_health() -> str: if __name__ == "__main__": import uvicorn - uvicorn.run(mcp.get_asgi_app(), host="0.0.0.0", port=8002) + uvicorn.run(mcp.sse_app(), host="0.0.0.0", port=8002) PY # ============================================================================= diff --git a/mcp_server.py b/mcp_server.py index 4c30779..7003ac9 100644 --- a/mcp_server.py +++ b/mcp_server.py @@ -199,5 +199,5 @@ def rag_health() -> str: if __name__ == "__main__": import uvicorn - app = mcp.get_asgi_app() + app = mcp.sse_app() uvicorn.run(app, host="0.0.0.0", port=8002) From 5bfedf2ceea7aa1f1defa2a121608efaeb1b2cde Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 21 Mar 2026 15:38:20 +0000 Subject: [PATCH 2/2] consolidate everything into the repo folder; add compose management comments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All scripts now default BASE to their own directory (SCRIPT_DIR) instead of ~/docker/ai-stack, so after a git clone the user never needs to change folders — docker-compose.yml, .env, settings, and data dirs all live next to the scripts. configure-searxng-safesearch.sh and configure-storage.sh do the same for standalone use (still honoured when called with BASE= from a parent script). docker-compose.yml (generated by both setup scripts) gains a comment block at the top listing the everyday docker compose commands: up -d / down / restart / stop / logs -f / pull / ps so the file itself is the reference for managing containers. .gitignore added to exclude generated files (docker-compose.yml, .env, requirements.txt, helper scripts) and data directories (workspace/, kiwix/, searxng/, gitea/, etc.) from git tracking. https://claude.ai/code/session_012gDnantBmFTWZGCiKyjazx --- .gitignore | 24 ++++++++++++++++++++++++ configure-searxng-safesearch.sh | 2 +- configure-storage.sh | 2 +- laptop_full_setup.sh | 19 +++++++++++++++++-- local-ai-setup.sh | 19 +++++++++++++++++-- 5 files changed, 60 insertions(+), 6 deletions(-) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..eba77b6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,24 @@ +# Generated by setup scripts — not tracked +docker-compose.yml +.env +requirements.txt +mcp_requirements.txt +start.sh +stop.sh +status.sh +pull-models.sh +Caddyfile.example +.ufw-done + +# Data directories +workspace/ +repos/ +papers/ +index/ +logs/ +kiwix/ +gitea/ +portainer-data/ +invokeai-data/ +invokeai-outputs/ +searxng/ diff --git a/configure-searxng-safesearch.sh b/configure-searxng-safesearch.sh index 7080d7c..e4549c4 100755 --- a/configure-searxng-safesearch.sh +++ b/configure-searxng-safesearch.sh @@ -34,7 +34,7 @@ LEVEL="moderate" DISABLE_CATS="" DISABLE_ENGINES_EXTRA="" ENABLE_ENGINES_EXTRA="" -BASE="${BASE:-$HOME/docker/ai-stack}" +BASE="${BASE:-$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)}" # ── Parse arguments ─────────────────────────────────────────────────────────── while [[ $# -gt 0 ]]; do diff --git a/configure-storage.sh b/configure-storage.sh index 667c245..535a1f7 100755 --- a/configure-storage.sh +++ b/configure-storage.sh @@ -10,7 +10,7 @@ ok() { echo -e "${GREEN}[OK]${NC} $*"; } warn() { echo -e "${YELLOW}[!!]${NC} $*"; } die() { echo -e "${RED}[ERR]${NC} $*"; exit 1; } -BASE="$HOME/docker/ai-stack" +BASE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" COMPOSE="$BASE/docker-compose.yml" [[ -f "$COMPOSE" ]] || die "docker-compose.yml not found at $BASE — run local-ai-setup.sh first" diff --git a/laptop_full_setup.sh b/laptop_full_setup.sh index 12f7549..3cd8f75 100755 --- a/laptop_full_setup.sh +++ b/laptop_full_setup.sh @@ -31,8 +31,8 @@ FORCE=false for arg in "$@"; do [[ "$arg" == "--force" ]] && FORCE=true; done # ── config ──────────────────────────────────────────────────────────────────── -BASE="$HOME/docker/ai-stack" SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +BASE="$SCRIPT_DIR" LOCAL_IP=$(ip route get 1.1.1.1 2>/dev/null | grep -oP 'src \K\S+' \ || hostname -I | awk '{print $1}') [[ -z "$LOCAL_IP" ]] && read -rp "Enter your LAN IP: " LOCAL_IP @@ -1033,8 +1033,23 @@ fi # Always written — it's the stack definition and safe to update cat > "$BASE/docker-compose.yml" << COMPOSE # Local AI Stack — generated $(date '+%Y-%m-%d') -# Edit \$BASE/.env to add API tokens. +# Edit .env in this folder to add API tokens. # GPU: OLLAMA_NUM_GPU=999 means "use all available VRAM" — auto-adapts to any GPU. +# +# ── Common commands (run from this folder) ───────────────────────────────────── +# Start everything: docker compose up -d +# Stop everything: docker compose down +# Restart one service: docker compose restart +# Stop one service: docker compose stop +# Start one service: docker compose up -d +# Follow all logs: docker compose logs -f +# Follow one service logs: docker compose logs -f +# Pull latest images: docker compose pull && docker compose up -d +# Show status: docker compose ps +# +# Services: ollama open-webui chromadb rag-server mcp-server +# searxng kiwix gitea invokeai portainer +# ─────────────────────────────────────────────────────────────────────────────── services: diff --git a/local-ai-setup.sh b/local-ai-setup.sh index cb3a202..8e21620 100755 --- a/local-ai-setup.sh +++ b/local-ai-setup.sh @@ -13,8 +13,8 @@ section() { echo -e "\n${BOLD}━━━ $* ━━━${NC}"; } FORCE=false; NO_PULL=false for a in "$@"; do [[ "$a" == "--force" ]] && FORCE=true; [[ "$a" == "--no-pull" ]] && NO_PULL=true; done -BASE="$HOME/docker/ai-stack" SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +BASE="$SCRIPT_DIR" LOCAL_IP=$(ip route get 1.1.1.1 2>/dev/null | grep -oP 'src \K\S+' || hostname -I | awk '{print $1}') [[ -z "$LOCAL_IP" ]] && read -rp "Enter LAN IP: " LOCAL_IP @@ -720,9 +720,24 @@ fi section "Docker Compose" # ============================================================================= cat > "$BASE/docker-compose.yml" << COMPOSE -# Local AI Stack — 2026-03-20 +# Local AI Stack — generated $(date '+%Y-%m-%d') # GPU: OLLAMA_NUM_GPU=999 uses all available VRAM automatically (V100/RTX/any) # Context: OLLAMA_NUM_CTX= set by detected VRAM (GB) +# +# ── Common commands (run from this folder) ───────────────────────────────────── +# Start everything: docker compose up -d +# Stop everything: docker compose down +# Restart one service: docker compose restart +# Stop one service: docker compose stop +# Start one service: docker compose up -d +# Follow all logs: docker compose logs -f +# Follow one service logs: docker compose logs -f +# Pull latest images: docker compose pull && docker compose up -d +# Show status: docker compose ps +# +# Services: ollama open-webui chromadb rag-server mcp-server +# searxng kiwix gitea invokeai portainer +# ─────────────────────────────────────────────────────────────────────────────── services: