Personal AI fitness coach that runs as a Telegram bot, powered by Claude Code (Max subscription). It integrates with Intervals.icu for training data, Google Calendar for scheduling, and web search for fitness research.
Built for triathletes (swim/bike/run) and weight lifters.
- Data-driven coaching — queries your Intervals.icu data (activities, training load, wellness, power curves) before giving advice
- Calendar-aware — checks Google Calendar to schedule workouts around your life
- Morning briefing — configurable daily cron that sends your training plan, checks wellness, and asks how you're feeling
- Self-improving — give feedback ("I'm vegetarian", "I have a knee injury") and the coach remembers it permanently by editing its own instructions
- Conversation memory — sessions persist across container restarts via SQLite
- Single-user — whitelist-based access, responds only to your Telegram account
Telegram --> Go bot (go-telegram/bot) --> claude -p subprocess --> Anthropic API
|
+-----------------+-----------------+
| | |
intervals-mcp gcal-mcp WebSearch
(Go binary) (Go binary) |
| | Web
Intervals.icu Google Calendar
All components run inside a single Docker container. Claude Code CLI authenticates via OAuth token (Max subscription — no API key billing).
- Docker and Docker Compose
- A Claude Max subscription (for Claude Code access)
- A Telegram account
- An Intervals.icu account with training data
This token lets Claude Code run headless inside Docker using your Max subscription.
# Install Claude Code if you haven't already
curl -fsSL https://downloads.claude.ai/claude-code-releases/bootstrap.sh | bash
# Generate a long-lived OAuth token for headless use
claude setup-tokenCopy the token (sk-ant-oat01-...) for use in the .env file.
- Open Telegram and message @BotFather
- Send
/newbotand follow the prompts (pick a name and a username ending inbot) - Copy the bot token (format:
123456789:AAH...)
Then get your numeric user ID:
- Message @userinfobot on Telegram
- It replies with your numeric ID (e.g.
7064223637)
- Log in to intervals.icu
- Go to Settings (gear icon)
- Scroll to the bottom — Developer Settings section
- Copy your API Key
- Your Athlete ID is shown there too (starts with
i, e.g.i518868). It's also visible in the URL when you view your profile.
The bot uses intervals-mcp, a Go-based MCP server that exposes 144 tools for the full Intervals.icu API. It's built from source during docker build.
See docs/google-calendar-setup.md for the full guide, including multi-account support (e.g. personal + work calendars).
Quick version:
- Create a Google Cloud project and enable the Google Calendar API
- Create OAuth credentials (Desktop app) and save to
./config/gcp-oauth.keys.json - Run the auth flow:
GOOGLE_OAUTH_CREDENTIALS=./config/gcp-oauth.keys.json \ GOOGLE_CALENDAR_MCP_TOKEN_PATH=./config/gcal-tokens.json \ go run ./cmd/gcal-mcp/ auth
- For multiple accounts, pass a nickname:
... auth personal,... auth work
cp .env.example .envEdit .env with your values:
# Required
TELEGRAM_BOT_TOKEN=123456:ABC-DEF...
ALLOWED_TELEGRAM_USER_IDS=7064223637
CLAUDE_CODE_OAUTH_TOKEN=sk-ant-oat01-...
INTERVALS_API_KEY=your-api-key
INTERVALS_ATHLETE_ID=i12345
TZ=Europe/Lisbon
# Optional
CLAUDE_MODEL=sonnet # sonnet/opus/haiku
CLAUDE_TIMEOUT=120 # seconds
BRIEFING_HOUR=7 # morning briefing time (0-23)
BRIEFING_MINUTE=0 # (0-59)
LOG_LEVEL=INFOdocker compose build
docker compose up -dCheck the logs:
docker compose logs -f| Command | Description |
|---|---|
/start |
Welcome message |
/new |
Reset conversation (start fresh session) |
| Any text | Chat with your AI coach |
- "What were my last 3 activities?"
- "How's my training load looking this week?"
- "I have a half marathon in 8 weeks, help me plan"
- "What should I eat before a long ride?"
- "Remember that I'm vegetarian" (self-improving — saved permanently)
The bot sends a daily check-in at the configured time (default 07:00). It:
- Checks your Intervals.icu training plan for the day
- Reviews recent wellness data (sleep, HRV, soreness)
- Looks at your training load and fatigue
- Recommends what to do
- Asks how you're feeling
Edit CLAUDE.md to change the coach's personality, knowledge areas, or behavior. Changes take effect on the next message — no rebuild needed.
The coach also edits its own CLAUDE.md when you give it feedback (e.g. dietary preferences, injury history, race goals). Check the "Athlete Profile" and "Learned Preferences" sections at the bottom of the file to see what it's learned.
ai_fitness_trainer/
├── cmd/
│ ├── bot/main.go # Telegram bot entrypoint
│ └── gcal-mcp/main.go # Google Calendar MCP server + auth CLI
├── internal/
│ ├── config/ # Environment variable loading
│ ├── store/ # SQLite session/KV/activity persistence
│ ├── claude/ # Claude CLI subprocess wrapper (stream-json)
│ ├── telegram/ # Bot handlers + message splitting
│ └── scheduler/ # Morning briefing cron + activity check
├── .env.example # Template for secrets
├── .mcp.json # MCP server config (intervals-icu + google calendar)
├── CLAUDE.md # Coach personality + self-improving memory
├── Dockerfile # Multi-stage: Go build + Claude CLI
├── docker-compose.yml
└── go.mod
Bot doesn't respond: Check docker compose logs -f for errors. Common issues:
- Invalid
CLAUDE_CODE_OAUTH_TOKEN— regenerate withclaude setup-token - Wrong
ALLOWED_TELEGRAM_USER_IDS— must be numeric, not username
Session errors after restart: The bot auto-recovers by starting a fresh session if --resume fails.
OAuth token expired: Re-run claude setup-token on your host, update .env, and docker compose restart.
Morning briefing not sending: Check that TZ is set correctly and the container clock matches. Verify with docker compose exec bot date.