Skip to main content
Glama

Garmin MCP Server

by zjromani
README.md5.99 kB
# Garmin MCP Server A webhook receiver and MCP (Model Context Protocol) server that collects Garmin health data and makes it available to ChatGPT through OpenAI's Remote MCP integration. ## Features - **Garmin Webhook Receiver**: Accepts health data from Garmin devices - **SQLite Persistence**: Stores data in SQLite database on EFS for reliability - **MCP Integration**: Provides health data to ChatGPT via OpenAI's Remote MCP - **AWS Deployment**: Runs on ECS Fargate with Terraform infrastructure - **Rate Limiting**: Protects webhook endpoints from abuse ## Architecture ``` Garmin Device → Webhook → SQLite DB → MCP Server → ChatGPT ``` ## Data Collected - Daily step count - Resting heart rate - Active calories burned - Sleep duration - Body battery levels ## Quick Start ### Local Development 1. Clone the repository: ```bash git clone https://github.com/yourusername/garmin-mcp.git cd garmin-mcp ``` 2. Install dependencies: ```bash npm install ``` 3. Set up environment variables: ```bash cp env.example .env # Edit .env with your configuration ``` 4. Build and run: ```bash npm run build npm start ``` ### Docker ```bash docker-compose up --build ``` ### Local HTTPS (Cloudflare Quick Tunnel) You can expose your local server with a temporary public HTTPS URL (no DNS or account needed): ```bash brew install cloudflared ./scripts/cloudflared-quick.sh ``` This prints a `https://<random>.trycloudflare.com` URL. Use it for testing endpoints: - Webhook: `https://<random>.trycloudflare.com/garmin/webhook` - Health: `https://<random>.trycloudflare.com/healthz` ## Environment Variables | Variable | Description | Required | |----------|-------------|----------| | `PORT` | Server port (default: 8080) | No | | `MCP_API_TOKEN` | Bearer token for MCP authentication | Yes | | `GARMIN_WEBHOOK_SECRET` | Secret for webhook signature verification | No | | `GARMIN_API_KEY` | Garmin Health API key | No | | `GARMIN_API_SECRET` | Garmin Health API secret | No | ## API Endpoints ### Webhook - `POST /garmin/webhook` - Receives Garmin health data ### MCP (Model Context Protocol) - `GET /mcp/tools` - Lists available tools - `POST /mcp/tools/call` - Executes a tool - `GET /mcp/sse` - Server-sent events endpoint ### Health - `GET /healthz` - Health check endpoint ## MCP Tools ### `garmin.getDailySummary` Get daily health summary for a user and date. **Parameters:** - `user_id` (string, required): User identifier - `date` (string, optional): Date in YYYY-MM-DD format (defaults to today) ### `garmin.getRecentDays` Get health data for the last N days. **Parameters:** - `user_id` (string, required): User identifier - `days` (number, optional): Number of days to retrieve (default: 7) ## Versioning & Deployment ### Versioning Strategy We use git-based versioning for Docker images: - **Git Tags**: Create semantic version tags (e.g., `v1.0.0`, `v1.1.0`) - **Docker Tags**: Images are tagged with both the git version and `latest` - **Rollback**: Can easily rollback by updating ECS task definition to use a previous version #### Creating a Release ```bash # 1. Create a git tag for the release git tag v1.0.0 # 2. Build and push with versioning cd terraform ./build-and-push.sh # 3. Deploy to ECS aws ecs update-service --cluster garmin-mcp-cluster --service garmin-mcp-service --force-new-deployment ``` #### Version History - **v0.1.0**: Initial release with SQLite persistence and integration tests - **Next**: `v1.0.0` - Production ready with Cloudflare Tunnel deployment #### Rollback Process ```bash # List available versions in ECR aws ecr describe-images --repository-name garmin-mcp --query 'imageDetails[*].imageTags' --output table # Update task definition to use specific version aws ecs update-service --cluster garmin-mcp-cluster --service garmin-mcp-service --task-definition garmin-mcp-task:REVISION_NUMBER ``` ### Deployment ### AWS with Terraform (Cost Optimized - ~$9/month) Our infrastructure uses a cost-optimized architecture with Cloudflare Tunnel for secure access: - **ECS Fargate**: Single task with 0.25 vCPU, 0.5GB RAM - **No NAT Gateway**: Saves ~$33/month by using public IP for outbound only - **Cloudflare Tunnel**: Provides secure HTTPS access without public inbound traffic - **EFS Storage**: SQLite database persistence (~$0.30/month) #### Prerequisites 1. **Cloudflare Account**: Create a tunnel and get the tunnel token 2. **AWS Credentials**: Configure AWS CLI access 3. **Domain**: Optional - for custom hostname instead of trycloudflare.com #### Deploy Steps 1. **Create Cloudflare Tunnel**: ```bash # Install cloudflared brew install cloudflared # Login to Cloudflare cloudflared tunnel login # Create tunnel cloudflared tunnel create garmin-mcp # Get tunnel token (save this) cloudflared tunnel token garmin-mcp ``` 2. **Configure Terraform**: ```bash cd terraform # Create terraform.tfvars with your tunnel token echo 'cloudflare_tunnel_token = "your-tunnel-token-here"' > terraform.tfvars ``` 3. **Deploy**: ```bash ./deploy.sh ``` 4. **Configure Tunnel Route** (optional): ```bash # For custom domain cloudflared tunnel route dns garmin-mcp webhook.yourdomain.com ``` #### Security Benefits - **No Public Inbound**: Security group blocks all incoming traffic - **Outbound Only**: Task can make outbound connections (Docker images, Cloudflare) - **Encrypted Tunnel**: All traffic encrypted through Cloudflare's edge - **Cost Effective**: No NAT Gateway or ALB required ### Manual Deployment 1. Build the Docker image 2. Push to ECR 3. Deploy to ECS Fargate ## Privacy This is a personal, non-commercial project. See [PRIVACY.md](PRIVACY.md) for details on data collection and usage. ## License Personal use only. This project is not intended for commercial use. ## Contributing This is a personal project, but suggestions and improvements are welcome through issues and discussions.

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/zjromani/garmin-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server