MCP (Model Context Protocol) server that enables AI assistants to access your Oura Ring health data through OAuth2-authenticated API calls.
Built for seamless integration with Poke and other MCP-compatible clients. Deploy to Railway for production use or run locally for development.
- OAuth2 with PKCE - Secure authentication with automatic token refresh
- 9 MCP Tools - Access sleep, readiness, activity, heart rate, workouts, and more
- Dual Transport Support - Both SSE and Streamable HTTP transports
- Token Encryption - AES-256-GCM encryption for OAuth tokens at rest
- Smart Caching - Reduce API calls with intelligent data caching
- Rate Limiting - Built-in protection against API quota exhaustion
- ngrok Support - Easy remote access for mobile and cloud integrations
- Node.js 18 or higher
- Oura Ring (all generations supported - Gen 2, Gen 3, and Gen 4)
- Oura account with API access
- Railway account (recommended for production deployment) or ngrok (for local development)
- Clone the repository:
git clone https://github.com/meimakes/oura-mcp-server.git
cd oura-mcp-server- Install dependencies:
npm install- Copy the example environment file:
cp .env.example .env- Generate required secrets:
# Generate AUTH_TOKEN
openssl rand -hex 32
# Generate TOKEN_ENCRYPTION_KEY
openssl rand -hex 32- Click the button below to deploy to Railway:
Or manually:
- Go to Railway
- Create new project from GitHub repo
- Connect your fork of this repository
- Generate required secrets locally:
# Generate AUTH_TOKEN
openssl rand -hex 32
# Generate TOKEN_ENCRYPTION_KEY
openssl rand -hex 32-
In Railway dashboard, add environment variables:
AUTH_TOKEN- Your generated auth tokenTOKEN_ENCRYPTION_KEY- Your generated encryption keyNODE_ENV- Set toproductionCORS_ORIGIN- Set to*or your specific domainPORT- Leave unset (Railway will auto-assign)
-
Wait for deployment to complete and note your Railway URL (e.g.,
https://your-app.up.railway.app)
- Go to https://cloud.ouraring.com/oauth/applications
- Click "Create New Application"
- Fill in:
- Application Name: "Personal MCP Server" (or your choice)
- Redirect URI:
https://your-app.up.railway.app/oauth/callback - Scopes: Select all available scopes
- Save the Client ID and Client Secret
Add these environment variables in Railway dashboard:
OURA_CLIENT_ID- Your Oura client IDOURA_CLIENT_SECRET- Your Oura client secretOURA_REDIRECT_URI-https://your-app.up.railway.app/oauth/callback
Railway will automatically redeploy with the new configuration.
- Install and authenticate ngrok:
ngrok config add-authtoken YOUR_NGROK_TOKEN- Start ngrok tunnel:
ngrok http 3001- Note your ngrok URL (e.g.,
https://your-domain.ngrok.dev)
Copy .env.example to .env and configure:
# MCP Server Authentication
AUTH_TOKEN=<generated-token>
# Oura OAuth Credentials
OURA_CLIENT_ID=<your-client-id>
OURA_CLIENT_SECRET=<your-client-secret>
OURA_REDIRECT_URI=https://your-domain.ngrok.dev/oauth/callback
# Server Configuration
PORT=3001
NODE_ENV=development
# Token Encryption
TOKEN_ENCRYPTION_KEY=<generated-key>
# CORS Origin
CORS_ORIGIN=*
# Logging (optional)
LOG_LEVEL=info # Options: error, warn, info, debug- Build the TypeScript code:
npm run build- Start the server:
npm startFor development with auto-reload:
npm run dev- Open your browser to your server's
/oauth/authorizeendpoint:- Railway:
https://your-app.up.railway.app/oauth/authorize - ngrok:
https://your-domain.ngrok.dev/oauth/authorize - Local:
http://localhost:3001/oauth/authorize
- Railway:
- Log in to your Oura account
- Approve the requested permissions
- You'll be redirected back with a success message
- Open Poke app
- Go to Settings → Integrations → Add Integration
- Select "Model Context Protocol (MCP)"
- Enter:
- Name: Oura
- Server URL:
https://your-app.up.railway.app/sse(or your deployment URL) - API Key: Your
AUTH_TOKENfrom environment variables
- Tap "Add Integration"
The server supports both SSE and Streamable HTTP transports for maximum compatibility.
Configure your MCP client with:
- Server URL:
https://your-app.up.railway.app/sse(or your deployment URL) - API Key (Bearer Token): Your
AUTH_TOKENfrom environment variables
Get user's personal information and ring details.
Get sleep data for a date range.
Parameters:
start_date(required): YYYY-MM-DDend_date(optional): YYYY-MM-DDinclude_hrv(optional): boolean
Get daily readiness scores.
Parameters:
start_date(required): YYYY-MM-DDend_date(optional): YYYY-MM-DD
Get activity data for a date range.
Parameters:
start_date(required): YYYY-MM-DDend_date(optional): YYYY-MM-DD
Get heart rate data in 5-minute intervals.
Parameters:
start_datetime(required): ISO 8601 formatend_datetime(optional): ISO 8601 format
Get workout sessions.
Parameters:
start_date(required): YYYY-MM-DDend_date(optional): YYYY-MM-DD
Get detailed sleep period data with heart rate and HRV.
Parameters:
start_date(required): YYYY-MM-DDend_date(optional): YYYY-MM-DD
Get user-created tags and notes.
Parameters:
start_date(required): YYYY-MM-DDend_date(optional): YYYY-MM-DD
Get AI-powered insights based on recent data.
Parameters:
days(optional): Number of days to analyze (default: 7)
GET /health
Returns server status, OAuth connection status, and cache statistics.
GET /oauth/authorize - Start OAuth flow
GET /oauth/callback - OAuth callback (automatic)
GET /oauth/status - Get connection status (requires auth)
POST /oauth/disconnect - Disconnect and clear tokens (requires auth)
The server supports both transport modes:
Streamable HTTP (recommended for Poke):
POST /sse - JSON-RPC requests with direct responses
Classic SSE:
GET /sse - Establish SSE connection
POST /message - Send JSON-RPC requests via session
All OAuth tokens are encrypted at rest using AES-256-GCM encryption.
MCP endpoints require Bearer token authentication:
Authorization: Bearer YOUR_AUTH_TOKEN
- MCP endpoints: 100 requests per 15 minutes per IP
- Oura API: 5000 requests per day (tracked automatically)
Configure allowed origins in .env with CORS_ORIGIN.
The server uses structured logging with configurable log levels:
error- Only critical errors (recommended for production)warn- Warnings and errorsinfo- Key operations, warnings, and errors (default)debug- Full verbosity including request/response bodies
Configure via the LOG_LEVEL environment variable:
LOG_LEVEL=info # Default - balanced logging
LOG_LEVEL=error # Production - minimal output
LOG_LEVEL=debug # Development - verbose debuggingWhat gets logged at each level:
- Error: Authentication failures, API errors, OAuth failures, rate limits
- Warn: Invalid API key attempts, missing SSE sessions
- Info: SSE connections, tool executions, OAuth operations, MCP method calls
- Debug: Full JSON-RPC requests/responses, connection lifecycle events
- Verify redirect URI matches exactly in Oura app settings
- Ensure your server is accessible (Railway deployed or ngrok running for local)
- Check
OURA_CLIENT_IDandOURA_CLIENT_SECRETare correct - Confirm
OURA_REDIRECT_URImatches your deployment URL
- Verify
TOKEN_ENCRYPTION_KEYis set correctly and hasn't changed - Check
tokens.jsonfile exists and is readable - Ensure refresh token hasn't been revoked in Oura account
- For Railway: Check that persistent storage is enabled
- Caching is built-in with 5-minute default TTL
- Reduce polling frequency in your MCP client
- Check rate limit headers in API responses
- Monitor usage at
/healthendpoint
Server not starting:
- Check Railway logs for errors
- Verify all required environment variables are set
- Ensure
PORTvariable is NOT set (Railway auto-assigns) - Check build logs for TypeScript compilation errors
OAuth redirect failing:
- Verify
OURA_REDIRECT_URIuses your Railway domain - Check that Railway deployment is public (not private networking only)
- Ensure HTTPS is used in redirect URI (Railway provides this automatically)
Tokens not persisting:
- Railway provides persistent storage by default for
tokens.json - Check application logs for file write errors
- Verify disk usage hasn't exceeded limits
ngrok connection issues:
- Restart ngrok tunnel
- Update
OURA_REDIRECT_URIif ngrok URL changed - Verify ngrok authentication token is valid
- Check ngrok isn't blocked by firewall
oura-mcp-server/
├── src/
│ ├── index.ts # Main server file
│ ├── oauth/
│ │ ├── handler.ts # OAuth flow handler
│ │ └── tokens.ts # Token management
│ ├── mcp/
│ │ ├── server.ts # MCP protocol implementation
│ │ └── tools.ts # Tool definitions
│ ├── oura/
│ │ ├── client.ts # Oura API client
│ │ └── types.ts # TypeScript types
│ ├── utils/
│ │ ├── encryption.ts # Token encryption
│ │ ├── cache.ts # Data caching
│ │ └── validation.ts # Input validation
│ └── middleware/
│ ├── auth.ts # Authentication middleware
│ └── errorHandler.ts # Error handling
├── .env # Environment variables (gitignored)
├── tokens.json # Encrypted tokens (gitignored)
├── package.json
├── tsconfig.json
└── README.md
npm testnpm run typechecknpm run lint- Cost: Free tier available, pay-as-you-grow
- Setup Time: ~5 minutes
- Benefits:
- Always available (24/7 uptime)
- Automatic deployments from GitHub
- Built-in HTTPS
- No server management required
- Persistent storage for tokens
- Best for: Production use, mobile access, sharing with others
- Cost: Free (ngrok free tier)
- Setup Time: ~10 minutes
- Benefits:
- Complete data privacy (tokens stay local)
- No hosting costs
- Full control over environment
- Limitations:
- Requires computer to be running
- ngrok URL changes on restart (free tier)
- Best for: Development, testing, personal use
- Deploy to DigitalOcean, AWS EC2, Google Cloud, etc.
- Always available with static IP
- More control but requires server management
docker build -t oura-mcp-server .
docker run -p 3001:3001 --env-file .env oura-mcp-serverCan be deployed to any Docker-compatible platform (Fly.io, Render, etc.)
MIT
For issues or questions:
- GitHub Issues: https://github.com/meimakes/oura-mcp-server/issues
- Oura API Docs: https://cloud.ouraring.com/docs
- MCP Protocol: https://modelcontextprotocol.io
Contributions are welcome! Please read CONTRIBUTING.md for details.