telegram: make MCP config portable across machines

- .mcp.json: switch telegram-* args to relative path (scripts/telegram-mcp.sh)
  so the committed config no longer pins a single user's home directory
- telegram-mcp.sh / regen_telegram_session.sh: drop hardcoded fallbacks for
  TELEGRAM_MCP_BIN / TELEGRAM_MCP_DIR; require them from .env with explicit
  error messages naming the missing variable
- .env.example: document per-machine path variables and per-account session
  strings, with a warning not to copy sessions between devices (Telegram
  revokes the auth key when one session is used from two IPs)
- CLAUDE.md: add a Bootstrap-on-a-new-machine section with the setup steps,
  the portability rationale, and a troubleshooting table for the common
  MCP / session failure modes

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Oleg Proskurin 2026-06-08 01:36:15 +07:00
parent 80c918d282
commit f4b75ebb7d
5 changed files with 98 additions and 16 deletions

View File

@ -1,5 +1,25 @@
# Copy this file to .env and fill in the values on each machine.
# .env is gitignored — secrets and per-machine paths never leave the device.
# --- Trello (shared across machines) ---
TRELLO_API_KEY=your_trello_api_key_here TRELLO_API_KEY=your_trello_api_key_here
TRELLO_TOKEN=your_trello_token_here TRELLO_TOKEN=your_trello_token_here
# --- Telegram API credentials (shared across machines: one app registration) ---
TELEGRAM_API_ID= TELEGRAM_API_ID=
TELEGRAM_API_HASH= TELEGRAM_API_HASH=
# --- Per-machine: absolute paths to the telegram-mcp install on THIS machine ---
# TELEGRAM_MCP_DIR — root of the telegram-mcp checkout (used by the session generator)
# TELEGRAM_MCP_BIN — the telegram-mcp executable inside that checkout's .venv
TELEGRAM_MCP_DIR=/absolute/path/to/telegram
TELEGRAM_MCP_BIN=/absolute/path/to/telegram/.venv/bin/telegram-mcp
# --- Per-machine Telegram session strings ---
# DO NOT copy these between machines. Each device/IP must have its own session,
# or Telegram will permanently revoke the auth key.
# Generate on THIS machine with:
# pnpm tg:session:usulsu -> writes TELEGRAM_SESSION_STRING
# pnpm tg:session:helper -> writes TELEGRAM_SESSION_STRING_HELPER
TELEGRAM_SESSION_STRING= TELEGRAM_SESSION_STRING=
TELEGRAM_SESSION_STRING_HELPER=

View File

@ -18,17 +18,11 @@
}, },
"telegram-usulpro": { "telegram-usulpro": {
"command": "bash", "command": "bash",
"args": [ "args": ["scripts/telegram-mcp.sh", "usulsu"]
"/home/usul/workspace/projects/cv-2026/scripts/telegram-mcp.sh",
"usulsu"
]
}, },
"telegram-helper": { "telegram-helper": {
"command": "bash", "command": "bash",
"args": [ "args": ["scripts/telegram-mcp.sh", "helper"]
"/home/usul/workspace/projects/cv-2026/scripts/telegram-mcp.sh",
"helper"
]
} }
} }
} }

View File

@ -136,6 +136,57 @@ After a run, triage `telegram_inbox.json` and promote promising postings to [`tr
--- ---
## Bootstrap on a new machine
This project runs on multiple machines (VPS, laptops) with different filesystem paths. The committed config is fully portable; everything machine-specific lives in `.env` and `.secrets/` (both gitignored). When Oleg sets up a new device, or when something Telegram-related breaks ("MCP not connecting", "session revoked", "TELEGRAM_MCP_BIN not set", etc.), walk through this.
### One-time setup on a fresh checkout
1. **`.env`** — copy from the template, then fill in:
```bash
cp .env.example .env
```
Required keys:
- `TRELLO_API_KEY`, `TRELLO_TOKEN` — same across machines (copy from another device's `.env`).
- `TELEGRAM_API_ID`, `TELEGRAM_API_HASH` — same across machines (one app registration).
- `TELEGRAM_MCP_DIR`**per-machine** absolute path to the `telegram-mcp` checkout on THIS machine.
- `TELEGRAM_MCP_BIN`**per-machine** absolute path to the `telegram-mcp` executable, typically `$TELEGRAM_MCP_DIR/.venv/bin/telegram-mcp`.
- `TELEGRAM_SESSION_STRING`, `TELEGRAM_SESSION_STRING_HELPER` — leave empty; generated in step 3.
2. **Node deps**:
```bash
pnpm install
```
3. **Generate Telegram sessions on THIS machine** (one per account):
```bash
pnpm tg:session:usulsu # QR-login as @usulpro (main)
pnpm tg:session:helper # QR-login as @samuishka (helper)
```
Each command opens a QR code in the terminal. Scan it from the corresponding Telegram account on Oleg's phone. The script writes the session string into `.env` and a copy into `.secrets/session_<account>.txt`.
**Critical rule — sessions are per-device:** never copy `TELEGRAM_SESSION_STRING*` between machines. A Telegram auth key used from two IPs gets permanently revoked, and **both** devices lose access until re-authorized. Always run the regen script on each new machine.
4. **Reconnect MCP servers** in Claude Code (`/mcp` → reconnect both `telegram-usulpro` and `telegram-helper`), or restart Claude Code.
### How paths stay portable
- `.mcp.json` uses a relative path (`scripts/telegram-mcp.sh`) — Claude Code launches project-scoped MCP servers with the project root as CWD, so this resolves correctly on any machine.
- `scripts/telegram-mcp.sh` and `scripts/regen_telegram_session.sh` read `TELEGRAM_MCP_BIN` / `TELEGRAM_MCP_DIR` from `.env`. They fail with an explicit error message if the variable is missing or points at a non-existent path — never guess a default.
### Troubleshooting cheat sheet
| Symptom | Cause | Fix |
|---|---|---|
| MCP server `telegram-usulpro` / `telegram-helper` won't start | `.env` missing, or `TELEGRAM_MCP_BIN` not set / not executable on this machine | Check the server's stderr in Claude Code logs; the script prints exactly what's missing. Fill in `.env`. |
| `session string for 'usulsu' is empty in .env` | Sessions weren't generated on this machine yet | `pnpm tg:session:usulsu` (and/or `:helper`) |
| `generator not found at .../.venv/bin/python` | `TELEGRAM_MCP_DIR` points at the wrong place, or `.venv` not created in the telegram-mcp checkout | Fix the path in `.env`; in the telegram-mcp checkout run its venv setup |
| Telegram suddenly logs us out / revokes the key | A session string got reused across two machines/IPs | Regen on each affected machine separately with `pnpm tg:session:<account>`; never copy session strings |
| `scripts/telegram-mcp.sh: No such file or directory` from Claude Code | `.mcp.json` got hardcoded back to an absolute path | Restore relative form: `"args": ["scripts/telegram-mcp.sh", "usulsu"]` |
| `list_telegram_channels.py` / `fetch_telegram_jobs.py` fail to auth | `TELEGRAM_SESSION_STRING` in `.env` doesn't match the usulsu account (these direct Telethon scripts always use usulsu) | Regen with `pnpm tg:session:usulsu` |
---
## Folder layout ## Folder layout
``` ```

View File

@ -27,20 +27,27 @@ case "$ACCOUNT" in
esac esac
PROJECT_ROOT="$(cd "$(dirname "$0")/.." && pwd)" PROJECT_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
TELEGRAM_DIR="${TELEGRAM_MCP_DIR:-/home/usul/workspace/projects/my-utils/telegram}"
ENV_FILE="$PROJECT_ROOT/.env" ENV_FILE="$PROJECT_ROOT/.env"
SECRETS_DIR="$PROJECT_ROOT/.secrets" SECRETS_DIR="$PROJECT_ROOT/.secrets"
OUT_FILE="$SECRETS_DIR/session_${ACCOUNT}.txt" OUT_FILE="$SECRETS_DIR/session_${ACCOUNT}.txt"
[ -f "$ENV_FILE" ] || { echo "missing $ENV_FILE" >&2; exit 1; } [ -f "$ENV_FILE" ] || { echo "missing $ENV_FILE (copy .env.example and fill it in)" >&2; exit 1; }
set -a; . "$ENV_FILE"; set +a set -a; . "$ENV_FILE"; set +a
if [ -z "${TELEGRAM_API_ID:-}" ] || [ -z "${TELEGRAM_API_HASH:-}" ]; then if [ -z "${TELEGRAM_API_ID:-}" ] || [ -z "${TELEGRAM_API_HASH:-}" ]; then
echo "TELEGRAM_API_ID / TELEGRAM_API_HASH missing in $ENV_FILE" >&2 echo "TELEGRAM_API_ID / TELEGRAM_API_HASH missing in $ENV_FILE" >&2
exit 1 exit 1
fi fi
TELEGRAM_DIR="${TELEGRAM_MCP_DIR:-}"
if [ -z "$TELEGRAM_DIR" ]; then
echo "TELEGRAM_MCP_DIR not set in $ENV_FILE" >&2
echo "point it at the telegram-mcp checkout on THIS machine, e.g.:" >&2
echo " TELEGRAM_MCP_DIR=/path/to/telegram" >&2
exit 1
fi
[ -x "$TELEGRAM_DIR/.venv/bin/python" ] || { [ -x "$TELEGRAM_DIR/.venv/bin/python" ] || {
echo "generator not found at $TELEGRAM_DIR/.venv/bin/python" >&2 echo "generator not found at $TELEGRAM_DIR/.venv/bin/python" >&2
echo "set TELEGRAM_MCP_DIR to your telegram-mcp checkout" >&2; exit 1; } echo "check TELEGRAM_MCP_DIR in $ENV_FILE and that its .venv is created" >&2; exit 1; }
mkdir -p "$SECRETS_DIR" mkdir -p "$SECRETS_DIR"
tmp="$(mktemp)"; trap 'rm -f "$tmp"' EXIT tmp="$(mktemp)"; trap 'rm -f "$tmp"' EXIT

View File

@ -1,20 +1,20 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# Launch the telegram-mcp server for one account, reading API creds and the # Launch the telegram-mcp server for one account, reading API creds and the
# session string from the project .env. Used by .mcp.json so that NO secrets # session string from the project .env. Used by .mcp.json so that NO secrets
# are hardcoded in the committed config. # and NO machine-specific paths are hardcoded in the committed config.
# #
# Usage (from .mcp.json): # Usage (from .mcp.json):
# bash scripts/telegram-mcp.sh usulsu # main account -> TELEGRAM_SESSION_STRING # bash scripts/telegram-mcp.sh usulsu # main account -> TELEGRAM_SESSION_STRING
# bash scripts/telegram-mcp.sh helper # samuishechka -> TELEGRAM_SESSION_STRING_HELPER # bash scripts/telegram-mcp.sh helper # samuishechka -> TELEGRAM_SESSION_STRING_HELPER
# #
# The telegram-mcp binary path can be overridden with TELEGRAM_MCP_BIN. # Per-machine config (must live in .env, not committed):
# TELEGRAM_MCP_BIN absolute path to the telegram-mcp executable on THIS machine
set -euo pipefail set -euo pipefail
ACCOUNT="${1:?usage: telegram-mcp.sh <usulsu|helper>}" ACCOUNT="${1:?usage: telegram-mcp.sh <usulsu|helper>}"
PROJECT_ROOT="$(cd "$(dirname "$0")/.." && pwd)" PROJECT_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
BIN="${TELEGRAM_MCP_BIN:-/home/usul/workspace/projects/my-utils/telegram/.venv/bin/telegram-mcp}"
[ -f "$PROJECT_ROOT/.env" ] || { echo "missing $PROJECT_ROOT/.env" >&2; exit 1; } [ -f "$PROJECT_ROOT/.env" ] || { echo "missing $PROJECT_ROOT/.env (copy .env.example and fill it in)" >&2; exit 1; }
set -a; . "$PROJECT_ROOT/.env"; set +a set -a; . "$PROJECT_ROOT/.env"; set +a
case "$ACCOUNT" in case "$ACCOUNT" in
@ -28,9 +28,19 @@ if [ -z "${TELEGRAM_API_ID:-}" ] || [ -z "${TELEGRAM_API_HASH:-}" ]; then
exit 1 exit 1
fi fi
if [ -z "${TELEGRAM_SESSION_STRING:-}" ]; then if [ -z "${TELEGRAM_SESSION_STRING:-}" ]; then
echo "session string for '$ACCOUNT' is empty in .env — regenerate it with:" >&2 echo "session string for '$ACCOUNT' is empty in .env — regenerate it on THIS machine with:" >&2
echo " pnpm tg:session:$ACCOUNT" >&2 echo " pnpm tg:session:$ACCOUNT" >&2
echo "(do not copy session strings between machines — Telegram revokes the key)" >&2
exit 1 exit 1
fi fi
BIN="${TELEGRAM_MCP_BIN:-}"
if [ -z "$BIN" ]; then
echo "TELEGRAM_MCP_BIN not set in $PROJECT_ROOT/.env" >&2
echo "point it at the telegram-mcp executable on THIS machine, e.g.:" >&2
echo " TELEGRAM_MCP_BIN=/path/to/telegram/.venv/bin/telegram-mcp" >&2
exit 1
fi
[ -x "$BIN" ] || { echo "TELEGRAM_MCP_BIN=$BIN is not executable on THIS machine" >&2; exit 1; }
exec "$BIN" exec "$BIN"