Skip to content

zjromani/garmin-mcp

Repository files navigation

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:
git clone https://github.com/yourusername/garmin-mcp.git
cd garmin-mcp
  1. Install dependencies:
npm install
  1. Set up environment variables:
cp env.example .env
# Edit .env with your configuration
  1. Build and run:
npm run build
npm start

Docker

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):

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

# 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

# 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:

    # 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:

    cd terraform
    
    # Create terraform.tfvars with your tunnel token
    echo 'cloudflare_tunnel_token = "your-tunnel-token-here"' > terraform.tfvars
  3. Deploy:

    ./deploy.sh
  4. Configure Tunnel Route (optional):

    # 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 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.

About

Garmin Health MCP

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors