diff --git a/lmcp.lua b/lmcp.lua index af66598..b04b24e 100644 --- a/lmcp.lua +++ b/lmcp.lua @@ -33,12 +33,17 @@ function lmcp.new(name, opts) self.port = opts.port or 8080 self.tools = {} self._session_id = nil - -- Auth: explicit opt > conf file > nil (no auth) + -- Auth: explicit opt > conf file > LMCP_TOKEN env > nil (no auth) if opts.auth_token then self._auth_token = opts.auth_token elseif opts.conf then local conf = read_conf(opts.conf) self._auth_token = conf['.godparticle'] + else + local env_token = os.getenv("LMCP_TOKEN") + if env_token and env_token ~= "" then + self._auth_token = env_token + end end return self end diff --git a/scripts/lmcp-install-macos.sh b/scripts/lmcp-install-macos.sh new file mode 100755 index 0000000..9e65d0f --- /dev/null +++ b/scripts/lmcp-install-macos.sh @@ -0,0 +1,129 @@ +#!/bin/bash +# Install lmcp on macOS via Homebrew. +# +# Idempotent. Pulls lua + luasocket from brew, copies lmcp library files +# into brew's share/lua/5.4/ tree, mints a Bearer token (or reuses an +# existing one at $(brew --prefix)/etc/lmcp/token), installs a LaunchAgent, +# starts the service, and prints the token for Claude Code MCP config. +# +# Usage (from lmcp repo root): +# ./scripts/lmcp-install-macos.sh +# +# Uninstall: +# launchctl unload ~/Library/LaunchAgents/de.reauktion.marfrit.lmcp.plist +# rm ~/Library/LaunchAgents/de.reauktion.marfrit.lmcp.plist +# rm $(brew --prefix)/etc/lmcp/token + +set -euo pipefail + +PREFIX=$(brew --prefix) +REPO=${REPO:-$(cd "$(dirname "$0")/.." && pwd)} +LABEL="de.reauktion.marfrit.lmcp" +PLIST="$HOME/Library/LaunchAgents/$LABEL.plist" +TOKEN_FILE="$PREFIX/etc/lmcp/token" +PORT="${LMCP_PORT:-8080}" +NAME="${LMCP_NAME:-$(hostname -s)-tools}" + +for f in lmcp.lua json.lua server.lua example_server.lua; do + [ -f "$REPO/$f" ] || { echo "error: $REPO/$f not found — run from lmcp repo root or set REPO="; exit 1; } +done + +echo "==> brew install lua + luasocket" +brew install --quiet lua luasocket + +LUA="$PREFIX/bin/lua" +[ -x "$LUA" ] || { echo "error: $LUA not executable after brew install"; exit 1; } + +echo "==> install library files into $PREFIX/share/lua/5.4/" +install -d "$PREFIX/share/lua/5.4" +install -m 644 "$REPO/lmcp.lua" "$PREFIX/share/lua/5.4/lmcp.lua" +install -m 644 "$REPO/json.lua" "$PREFIX/share/lua/5.4/json.lua" +install -m 644 "$REPO/server.lua" "$PREFIX/share/lua/5.4/server.lua" +install -m 755 "$REPO/example_server.lua" "$PREFIX/bin/lmcp-example" + +# Token: retain existing, otherwise mint 32 bytes of hex. +if [ -r "$TOKEN_FILE" ]; then + TOKEN=$(cat "$TOKEN_FILE") + echo "==> reusing token from $TOKEN_FILE" +else + install -d -m 700 "$(dirname "$TOKEN_FILE")" + TOKEN=$(openssl rand -hex 32) + umask 077 + printf '%s\n' "$TOKEN" > "$TOKEN_FILE" + chmod 600 "$TOKEN_FILE" + echo "==> minted new token, stored at $TOKEN_FILE (0600)" +fi + +echo "==> write LaunchAgent $PLIST" +mkdir -p "$HOME/Library/LaunchAgents" +cat > "$PLIST" < + + + + Label + $LABEL + ProgramArguments + + $LUA + $PREFIX/share/lua/5.4/server.lua + + EnvironmentVariables + + LMCP_PORT + $PORT + LMCP_NAME + $NAME + LMCP_TOKEN + $TOKEN + + RunAtLoad + + KeepAlive + + StandardOutPath + /tmp/lmcp.log + StandardErrorPath + /tmp/lmcp.err + + +PLIST +chmod 600 "$PLIST" # plist contains token + +echo "==> (re)load LaunchAgent" +launchctl unload "$PLIST" 2>/dev/null || true +launchctl load "$PLIST" + +sleep 1 +echo "==> smoke test (unauth expected 401, Bearer expected 200)" +unauth=$(curl -s -o /dev/null -w '%{http_code}' -X POST "http://127.0.0.1:$PORT/mcp" \ + -H 'Content-Type: application/json' \ + -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' || echo "000") +auth=$(curl -s -X POST "http://127.0.0.1:$PORT/mcp" \ + -H "Authorization: Bearer $TOKEN" \ + -H 'Content-Type: application/json' \ + -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' || true) + +if [ "$unauth" = "401" ] && echo "$auth" | grep -q '"tools"'; then + echo "OK — lmcp listening on :$PORT as $NAME, Bearer-gated" +else + echo "smoke test failed (unauth=$unauth, auth body below)" + echo "$auth" | head -c 500; echo + tail -20 /tmp/lmcp.err 2>/dev/null || true + exit 1 +fi + +cat <