Files
poke-mail/start.sh
T
0xK 581dd0b89a fix: optional MCP_API_KEY in tunnel mode + address Copilot review issues
- start.sh: detect POKE_TUNNEL env var; skip MCP_API_KEY requirement and
  auth when running via poke tunnel (server.py reads the same var)
- start.sh: pass POKE_TOKEN into Python via env var + use json.dumps to
  safely escape quotes/backslashes in YAML (fixes shell-interpolation
  injection risk, Copilot issue #5 / start.sh:72)
- start.sh: anchor MCP_API_KEY guard to non-commented line-start
  assignments and also detect empty value (Copilot issues #1, #8 /
  start.sh:104)
- start.sh: anchor re.sub pattern with re.MULTILINE so only the actual
  assignment line is rewritten, not mid-line occurrences (Copilot
  issue #2)
- start.sh: check re.sub replacement count, warn when poke_api_key key
  is missing from config.yml (Copilot issue #6)
- start.sh: guard npm/npx usage with command -v check; fall back to npx
  poke instead of hard-failing (Copilot issue #3)
- start.sh: use python3 consistently for server.py (Copilot issue #9 /
  start.sh:134)
- start.sh: prefer npx poke tunnel; check command -v poke and fall back
  gracefully (Copilot issue #10 / start.sh:135)
- server.py: honour POKE_TUNNEL=1 — skip bearer-token auth so the poke
  tunnel handles identity; MCP_API_KEY becomes optional in that mode
- README.md: add Node.js/npm prerequisite note (Copilot issue #4)
- README.md: clarify server starts on first run; update AI agent prompt
  (Copilot issue #11 / README.md:48)
2026-03-24 11:40:26 +01:00

184 lines
6.9 KiB
Bash
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env bash
set -euo pipefail
cd "$(dirname "$0")"
# ── Check for updates ─────────────────────────────────────────────────────────
REPO="kacperkwapisz/poke-mail"
LOCAL_SHA=$(git rev-parse HEAD 2>/dev/null || echo "unknown")
REMOTE_SHA=$(curl -sf "https://api.github.com/repos/${REPO}/commits/main" \
| grep -m1 '"sha"' | cut -d'"' -f4 || echo "")
if [ -n "$REMOTE_SHA" ] && [ "$REMOTE_SHA" != "$LOCAL_SHA" ]; then
echo "⚡ A newer version of poke-mail is available."
echo " Local: ${LOCAL_SHA:0:7}"
echo " Remote: ${REMOTE_SHA:0:7}"
echo " Run 'git pull' to update."
echo ""
fi
# ── One-time setup (skipped on subsequent runs) ───────────────────────────────
# 1. Python virtualenv + dependencies
if [ ! -d .venv ]; then
echo "First run — setting up poke-mail..."
echo ""
echo "Creating Python virtualenv (.venv)..."
python3 -m venv .venv
fi
source .venv/bin/activate
if ! python3 -c "import fastmcp" &>/dev/null 2>&1; then
echo "Installing Python dependencies..."
pip install -q -r requirements.txt
echo " ✓ Dependencies installed"
echo ""
fi
# 2. config.yml — copy example if missing
if [ ! -f config.yml ]; then
echo "Copying config.example.yml → config.yml..."
cp config.example.yml config.yml
echo " ✓ config.yml created"
echo ""
fi
# 3. Poke API key — read from 'poke login' credentials, inject into config.yml
# The 'poke' npm package writes ~/.config/poke/credentials.json { "token": "..." }
# after 'npx poke login'. We auto-read it so the user never has to copy/paste.
if grep -q 'your-api-key-here' config.yml 2>/dev/null; then
POKE_CREDENTIALS_FILE="${XDG_CONFIG_HOME:-$HOME/.config}/poke/credentials.json"
POKE_TOKEN=""
if [ -f "$POKE_CREDENTIALS_FILE" ]; then
POKE_TOKEN=$(python3 -c "
import json, sys
try:
data = json.load(open('$POKE_CREDENTIALS_FILE'))
print(data.get('token', ''))
except Exception:
print('')
" 2>/dev/null || true)
fi
if [ -n "$POKE_TOKEN" ]; then
echo " ✓ Poke API key detected from 'poke login'"
# Pass token via env var to avoid shell-interpolation injection in Python source.
# json.dumps handles quoting/escaping so the result is valid YAML.
POKE_TOKEN="$POKE_TOKEN" python3 - <<'PYEOF'
import os, re, json
token = os.environ['POKE_TOKEN']
with open('config.yml', 'r') as f:
content = f.read()
pattern = r'(?m)^([ \t]*poke_api_key:[ \t*])[^\n]+'
new_content, n = re.subn(pattern, lambda m: m.group(1) + json.dumps(token), content)
if n == 0:
print(' ⚠ Warning: poke_api_key key not found in config.yml — update it manually.')
with open('config.yml', 'w') as f:
f.write(new_content)
PYEOF
echo " ✓ poke_api_key written to config.yml"
echo ""
else
echo " ⚠ poke_api_key not set in config.yml."
echo " Run 'npx poke login' then restart, or paste your key from poke.com/settings/advanced"
echo ""
printf " Poke API key (leave blank to set manually later): "
read -r POKE_TOKEN_INPUT
POKE_TOKEN_INPUT=$(echo "$POKE_TOKEN_INPUT" | tr -d '[:space:]')
if [ -n "$POKE_TOKEN_INPUT" ]; then
POKE_TOKEN="$POKE_TOKEN_INPUT" python3 - <<'PYEOF'
import os, re, json
token = os.environ['POKE_TOKEN']
with open('config.yml', 'r') as f:
content = f.read()
pattern = r'(?m)^([ \t]*poke_api_key:[ \t*])[^\n]+'
new_content, n = re.subn(pattern, lambda m: m.group(1) + json.dumps(token), content)
if n == 0:
print(' ⚠ Warning: poke_api_key key not found in config.yml — update it manually.')
with open('config.yml', 'w') as f:
f.write(new_content)
PYEOF
echo " ✓ poke_api_key saved to config.yml"
fi
echo ""
fi
fi
# 4. MCP_API_KEY — generate once and persist to .env
# Regenerate when: .env is missing, contains the placeholder, or has an
# empty assignment (MCP_API_KEY=) which would still fail at the :? check.
# Anchored to non-commented, line-start assignments only.
if [ ! -f .env ] \
|| grep -Eq '^[[:space:]]*MCP_API_KEY=your-secret-key-here' .env 2>/dev/null \
|| ! grep -Eq '^[[:space:]]*MCP_API_KEY=.+' .env 2>/dev/null; then
RANDOM_KEY=$(python3 -c "
import secrets, string
alphabet = string.ascii_letters + string.digits
print(''.join(secrets.choice(alphabet) for _ in range(48)))
")
if [ -f .env ]; then
# Anchor to line-start with MULTILINE so only the actual assignment is updated.
RANDOM_KEY="$RANDOM_KEY" python3 - <<'PYEOF'
import os, re
new_key = os.environ['RANDOM_KEY']
with open('.env', 'r') as f:
content = f.read()
new_content, n = re.subn(r'(?m)^[[:space:]]*MCP_API_KEY=.*', f'MCP_API_KEY={new_key}', content)
if n == 0:
new_content += f'MCP_API_KEY={new_key}\n'
with open('.env', 'w') as f:
f.write(new_content)
PYEOF
else
echo "MCP_API_KEY=${RANDOM_KEY}" > .env
fi
echo " ✓ MCP_API_KEY generated and saved to .env"
echo ""
fi
# ── Load .env ─────────────────────────────────────────────────────────────────
if [ -f .env ]; then
set -a
source .env
set +a
fi
# ── Tunnel-mode detection ─────────────────────────────────────────────────────
# When POKE_TUNNEL=1 the poke tunnel handles auth — MCP_API_KEY is optional.
# In all other modes (direct HTTP, Docker, etc.) it is required.
POKE_TUNNEL="${POKE_TUNNEL:-1}" # default to tunnel mode since start.sh always tunnels
if [ "${POKE_TUNNEL}" != "1" ]; then
: "${MCP_API_KEY:?MCP_API_KEY is not set — add it to .env or export it}"
else
# In tunnel mode warn when the key is absent but don't abort.
if [ -z "${MCP_API_KEY:-}" ]; then
echo " MCP_API_KEY not set — server runs unauthenticated (safe: poke tunnel handles auth)."
fi
export POKE_TUNNEL
fi
# ── Start server + tunnel ─────────────────────────────────────────────────────
echo "Starting poke-mail server..."
python3 src/server.py &
SERVER_PID=$!
trap "kill $SERVER_PID 2>/dev/null" EXIT
# Wait for server to be ready
sleep 2
# Tunnel to Poke — prefer the globally-installed poke binary; fall back to npx.
echo "Starting tunnel to Poke..."
if command -v poke &>/dev/null; then
poke tunnel http://localhost:3000/mcp --name "poke-mail"
else
echo " 'poke' binary not found in PATH — using npx poke (requires Node.js)."
if ! command -v npx &>/dev/null; then
echo " ✗ Neither 'poke' nor 'npx' found. Install Node.js (nodejs.org) and run:"
echo " npm install -g poke OR npx poke tunnel ..."
exit 1
fi
npx --yes poke tunnel http://localhost:3000/mcp --name "poke-mail"
fi