{
  "markdown": "# polar-flow-server\n\nSelf-hosted health analytics for Polar devices — own your data, analyze it against your own baselines, and let your AI assistant read it.\n\n[![Tests](https://github.com/StuMason/polar-flow-server/actions/workflows/tests.yml/badge.svg)](https://github.com/StuMason/polar-flow-server/actions/workflows/tests.yml)\n[![Docs](https://img.shields.io/badge/docs-mkdocs-blue)](https://stumason.github.io/polar-flow-server/)\n[![Docker](https://img.shields.io/docker/v/stumason/polar-flow-server?label=docker)](https://hub.docker.com/r/stumason/polar-flow-server)\n[![MCP](https://img.shields.io/badge/MCP-2026--07--28-6549d5)](https://stumason.github.io/polar-flow-server/mcp-server/)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\n![Dashboard](docs/assets/dashboard-today.png)\n\n**[Full Documentation](https://stumason.github.io/polar-flow-server/)** · [MCP Server](https://stumason.github.io/polar-flow-server/mcp-server/) · [Integration Guide](https://stumason.github.io/polar-flow-server/integration/) · [API Reference](https://stumason.github.io/polar-flow-server/api/overview/)\n\n## What This Does\n\nYour watch knows more about you than you do — and Polar's API only lets you see the last 28-30 days of it. This server syncs everything, keeps it forever, and turns it into answers:\n\n1. Syncs all **13 Polar API endpoints** automatically — sleep, HRV, activity, workouts, SpO2, ECG, skin temperature, the lot\n2. Stores everything in PostgreSQL. Your data, your server, no cloud between you and it\n3. Computes **personal baselines** (rolling averages, IQR anomaly bounds) so \"is this normal?\" means normal *for you*\n4. Ships a **built-in MCP server with OAuth sign-in** — ask Claude \"should I train hard today?\" and it answers from your overnight HRV vs your baseline\n5. Admin dashboard (HTMX), REST API, per-user API keys, multi-user ready\n\n## Ask Your AI About Your Body (MCP)\n\nA built-in [Model Context Protocol](https://modelcontextprotocol.io) server — protocol revision **2026-07-28**, streamable HTTP — runs inside the main server at `/mcp`. Ten curated tools cover the one-shot health assessment, sleep, recovery, activity, workouts, seven biosensing streams, personal baselines, patterns/anomalies, and sync control.\n\nIn clients that render [MCP Apps](https://modelcontextprotocol.io/extensions/apps/overview) (claude.ai, Claude Desktop, VS Code), asking \"how am I doing?\" draws an actual card in the conversation:\n\n![MCP Apps card](docs/assets/mcp-apps-card.png)\n\n**Connecting is a sign-in, not a paste.** With `BASE_URL` set, the server is its own OAuth 2.1 authorization server: add `https://your-server/mcp` as a custom connector in Claude Desktop or claude.ai, click **Connect**, log in on *your* server, approve the consent screen. Tokens are user-scoped, expire hourly, refresh automatically, and every connected app is revocable from Settings. API keys still work for headless clients:\n\n```bash\nclaude mcp add polar-health https://your-server.example.com/mcp \\\n  --transport http \\\n  --header \"X-API-Key: pfk_your_key_here\"\n```\n\nFull setup in the [MCP docs](https://stumason.github.io/polar-flow-server/mcp-server/).\n\n## Architecture\n\n```\nPolar API → polar-flow SDK → Sync Service → PostgreSQL\n                                                  ↓\n                                           Admin Dashboard (HTMX)\n                                                  ↓\n                                             REST API\n```\n\n**Stack:**\n- Litestar (async web framework)\n- SQLAlchemy 2.0 (async ORM)\n- PostgreSQL\n- HTMX + Tailwind (admin UI)\n- polar-flow SDK v1.5.0\n\n> **Don't fancy running a server?** A hosted version is in the works — [join the waitlist](https://pulse.stumason.dev). Self-hosting stays free forever.\n\n## Quick Start\n\n### Option 1: Docker (Recommended)\n\n```bash\n# Pull and run\ncurl -O https://raw.githubusercontent.com/StuMason/polar-flow-server/main/docker-compose.prod.yml\ndocker-compose -f docker-compose.prod.yml up -d\n\n# That's it. Open http://localhost:8000/admin\n```\n\n### Option 2: From Source\n\n```bash\ngit clone https://github.com/StuMason/polar-flow-server.git\ncd polar-flow-server\ndocker-compose up -d\n```\n\n### Setup\n\n1. Open http://localhost:8000/admin\n2. Get Polar credentials from [admin.polaraccesslink.com](https://admin.polaraccesslink.com) (set redirect URI to `http://localhost:8000/admin/oauth/callback`)\n3. Enter credentials and click \"Connect with Polar\"\n4. Hit \"Sync Now\" to pull your data\n\nThe server syncs data every hour automatically.\n\n## Dashboard\n\nThe admin panel at `/admin/dashboard` is organised into tabs (with a\nfloating tab bar on mobile):\n\n- **Overview** - stat tiles (HRV, resting HR, SpO2, skin temp, steps, strain,\n  sleep score, alertness...), Today's Readiness recommendations, and\n  \"Today at a Glance\" mini-charts (sleep stages, heart rate, steps)\n- **Trends & Baselines** - personal baselines and detected patterns\n- **Sleep** - sleep score and stage-duration charts\n- **Heart Rate** - daily HR, HRV and ANS charge charts, biosensing panel\n- **Training Load** - activity and cardio load charts\n\n![Trends and baselines](docs/assets/dashboard-trends.png)\n\nCharts have a selectable 7/14/30-day range and CSV export. API keys are\nmanaged from the settings page, with rate limit tracking. All frontend\nassets are vendored - the dashboard works offline and on a LAN with no\nCDNs.\n\n## Data Synced (13 Endpoints)\n\n| Endpoint | Data |\n|----------|------|\n| **Sleep** | Score, stages (light/deep/REM), duration |\n| **Nightly Recharge** | HRV, ANS charge, recovery status |\n| **Daily Activity** | Steps, distance, calories, active time |\n| **Exercises** | Sport, duration, HR zones, training load |\n| **Cardio Load** | Strain, tolerance, load ratio, status |\n| **SleepWise Alertness** | Hourly alertness predictions |\n| **SleepWise Bedtime** | Optimal sleep timing recommendations |\n| **Activity Samples** | Minute-by-minute step data |\n| **Continuous HR** | All-day heart rate (5-min intervals) |\n| **SpO2** | Blood oxygen tests (compatible devices) |\n| **ECG** | Electrocardiogram tests (compatible devices) |\n| **Body Temperature** | Continuous body temperature |\n| **Skin Temperature** | Nightly skin temperature with baseline deviation |\n\n## Configuration\n\n### Required Environment Variables\n\n| Variable | Description | Required |\n|----------|-------------|----------|\n| `DATABASE_URL` | PostgreSQL connection string | Yes |\n| `ENCRYPTION_KEY` | 32-byte Fernet key for token encryption | **Yes (production)** |\n\nGenerate an encryption key:\n```bash\npython -c \"from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())\"\n```\n\n### Optional Environment Variables\n\n| Variable | Description | Default |\n|----------|-------------|---------|\n| `DEPLOYMENT_MODE` | `self_hosted` or `saas` | `self_hosted` |\n| `SYNC_INTERVAL_HOURS` | Auto-sync frequency | `1` |\n| `SYNC_ON_STARTUP` | Sync when server starts | `false` |\n| `SYNC_DAYS_LOOKBACK` | Days of history to sync | `28` |\n| `LOG_LEVEL` | Logging verbosity | `INFO` |\n| `API_KEY` | Master API key (bypasses rate limits) | None |\n\n## API Authentication\n\n**API endpoints require authentication.** Health data should never be publicly accessible.\n\n### Authentication Methods\n\n1. **Per-User API Keys** (recommended) - Create from the admin dashboard or via OAuth flow\n2. **Master API Key** - Set `API_KEY` env var for full access (bypasses rate limits)\n\n### Using API Keys\n\n```bash\n# With per-user API key (includes rate limit headers)\ncurl -H \"X-API-Key: pfk_your_api_key_here\" \\\n  http://localhost:8000/api/v1/users/{user_id}/sleep?days=7\n\n# Response headers include:\n# X-RateLimit-Limit: 1000\n# X-RateLimit-Remaining: 999\n# X-RateLimit-Reset: 1704067200\n```\n\n### Rate Limiting\n\n- Default: 1000 requests per hour per API key\n- Rate limits reset hourly\n- Master API key (`API_KEY` env var) bypasses rate limiting\n- Rate limit info returned in response headers\n\n## OAuth Integration (SaaS / Multi-User)\n\nFor applications that need to integrate with polar-flow-server (e.g., Laravel, mobile apps, web frontends).\n\nThis allows **any Polar user** to connect their account to your application.\n\n### OAuth Flow\n\n```\n┌─────────────────┐     ┌─────────────────────┐     ┌─────────────────┐\n│  Your App       │────▶│  polar-flow-server  │────▶│  Polar Flow     │\n│  (Laravel etc)  │     │                     │     │  (OAuth)        │\n│                 │◀────│                     │◀────│                 │\n└─────────────────┘     └─────────────────────┘     └─────────────────┘\n```\n\n**Step 1: Redirect user to start OAuth**\n\n```\nGET /oauth/start?callback_url=https://yourapp.com/callback&client_id=your-app-name\n```\n\n| Parameter | Required | Description |\n|-----------|----------|-------------|\n| `callback_url` | Yes | Where to redirect after OAuth (your app's callback endpoint) |\n| `client_id` | No | Identifier for your app (validated during exchange) |\n\n**Step 2: User authorizes on Polar**\n\nUser is redirected to Polar, logs in with their credentials, and authorizes your app.\n\n**Step 3: User redirected to your callback**\n\n```\nhttps://yourapp.com/callback?code=TEMP_CODE_HERE\n```\n\n**Step 4: Exchange temp code for API key (server-to-server)**\n\n```bash\nPOST /oauth/exchange\nContent-Type: application/json\n\n{\n  \"code\": \"TEMP_CODE_HERE\",\n  \"client_id\": \"your-app-name\"\n}\n```\n\nResponse:\n```json\n{\n  \"api_key\": \"pfk_abc123...\",\n  \"polar_user_id\": \"12345678\",\n  \"expires_at\": null\n}\n```\n\n**Step 5: Store and use the API key**\n\nStore `api_key` and `polar_user_id` for this user. Use the API key for all data requests:\n\n```bash\ncurl -H \"X-API-Key: pfk_abc123...\" \\\n  \"https://your-polar-server.com/api/v1/users/12345678/sleep?days=7\"\n```\n\n### Polar Admin Setup\n\nIn [admin.polaraccesslink.com](https://admin.polaraccesslink.com), set your app's redirect URI to:\n\n```\nhttps://your-polar-server.com/oauth/callback\n```\n\n### Key Management\n\n```bash\n# Get key info\nGET /api/v1/users/{user_id}/api-key/info\nX-API-Key: pfk_...\n\n# Regenerate key (invalidates old key)\nPOST /api/v1/users/{user_id}/api-key/regenerate\nX-API-Key: pfk_...\n\n# Revoke key\nPOST /api/v1/users/{user_id}/api-key/revoke\nX-API-Key: pfk_...\n```\n\n## API Endpoints\n\n```bash\n# Health check (no auth required)\ncurl http://localhost:8000/health\n\n# Get sleep data (last 7 days)\ncurl -H \"X-API-Key: pfk_...\" \\\n  \"http://localhost:8000/api/v1/users/{user_id}/sleep?days=7\"\n\n# Get activity data\ncurl -H \"X-API-Key: pfk_...\" \\\n  \"http://localhost:8000/api/v1/users/{user_id}/activity?days=7\"\n\n# Get nightly recharge (HRV)\ncurl -H \"X-API-Key: pfk_...\" \\\n  \"http://localhost:8000/api/v1/users/{user_id}/recharge?days=7\"\n\n# Get exercises\ncurl -H \"X-API-Key: pfk_...\" \\\n  \"http://localhost:8000/api/v1/users/{user_id}/exercises?days=30\"\n\n# Export summary\ncurl -H \"X-API-Key: pfk_...\" \\\n  \"http://localhost:8000/api/v1/users/{user_id}/export/summary?days=30\"\n```\n\n## Development\n\n```bash\n# Install dependencies\nuv sync --all-extras\n\n# Start PostgreSQL\ndocker-compose up -d postgres\n\n# Run server with hot reload\nuv run uvicorn polar_flow_server.app:app --reload\n\n# Run tests\nuv run pytest\n\n# Type check\nuv run mypy src/polar_flow_server\n\n# Lint\nuv run ruff check src/\n```\n\n## Production Deployment\n\nDeploy anywhere that runs Docker:\n\n```bash\n# Download and run\ncurl -O https://raw.githubusercontent.com/StuMason/polar-flow-server/main/docker-compose.prod.yml\ndocker-compose -f docker-compose.prod.yml up -d\n```\n\n**Coolify, Railway, Render, etc.** - Point at the GitHub repo, it builds from the Dockerfile.\n\n**Required for production:**\n- Set `ENCRYPTION_KEY` environment variable (tokens won't persist across restarts otherwise)\n- Set `DATABASE_URL` to your PostgreSQL instance\n\n**Database migrations** run automatically on startup.\n\n## Multi-Tenancy\n\nThe server supports multiple users out of the box:\n\n- Every table includes `user_id` column\n- All queries scoped by `user_id`\n- Per-user API keys ensure users can only access their own data\n- Self-hosted: typically one user\n- Multi-user: many users, same codebase\n\n## Built With\n\n- [polar-flow](https://github.com/StuMason/polar-flow) - Python SDK for Polar AccessLink API\n- [Litestar](https://litestar.dev/) - Async web framework\n- [SQLAlchemy](https://www.sqlalchemy.org/) - Async ORM\n- [HTMX](https://htmx.org/) - Admin UI interactions\n- [Tailwind CSS](https://tailwindcss.com/) - Styling\n\n## License\n\nMIT\n",
  "bytes": 12401,
  "sha": "e0041fc167fa623c25fb17e606a04d556c10e2168ac28c544da438d838db9967",
  "repo_slug": "stumason/polar-flow-server",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_stumason_polar_flow_server_f351b8f4/readme"
}