Skip to main content
Glama

ouraring-mcp

MCP (Model Context Protocol) server that lets an AI assistant read your own Oura Ring data — sleep, readiness, activity, heart rate, SpO2, stress, workouts, sessions — so you can ask questions and get summaries in natural language. Personal, single-user, non-commercial use. Data is fetched on demand and not stored or shared.

Built for the Oura API v2 (api.ouraring.com), same stdio-MCP layout as parksapi-mcp.

Tools

Tool

What it does

Oura scope(s)

oura_status

Credential check (no network)

—

list_collections

Every readable collection + required scope

—

get_personal_info

Profile (age, weight, height, email)

personal, email

get_daily

Daily summaries: sleep, readiness, activity, stress, spo2, resilience, cardiovascular_age

daily (+ spo2/stress/heart_health)

get_collection

Any collection: sleep, sleep_time, workout, session, tag, enhanced_tag, heartrate, rest_mode_period, ring_configuration, ring_battery_level, vo2_max, daily_*; single doc via document_id

per collection

get_recent_summary

Last N days (sleep + readiness + activity + stress) in one call

daily

All tools return JSON as text and are read-only. Unknown collections/types return { isError: true } with a plain message, never a protocol error. Date params are YYYY-MM-DD; heartrate also accepts ISO datetimes + latest.

Related MCP server: Oura Ring MCP Server

Requirements

Node 22+.

Setup

npm install
npm run build

OAuth2 (only method — Oura retired PATs Dec 2025)

Register at https://cloud.ouraring.com/oauth/applications with:

  • Redirect URI: http://localhost:8080/callback

  • Scopes: email personal daily heartrate workout tag session spo2 ring_configuration stress heart_health

  • Privacy / Terms: this repo's PRIVACY.md / TERMS.md

Then:

npm run auth:oura       # prints the authorize URL
# open the URL, approve, copy ?code= from the localhost redirect, then:
npm run auth:oura -- --code=PASTE_CODE_HERE

auth:oura reads OURARING_CLIENT_ID/OURARING_CLIENT_SECRET from the environment → local .env → ~/.hermes/.env, then prints OURARING_ACCESS_TOKEN (short-lived) + OURARING_REFRESH_TOKEN (long-lived). The server auto-refreshes via the refresh token when client credentials are present — no manual refresh needed.

Install in your tools

Build once (npm install && npm run build), then point any client at dist/index.js via stdio with OURARING_REFRESH_TOKEN + OURARING_CLIENT_ID + OURARING_CLIENT_SECRET in the environment (server auto-refreshes; access token alone expira rápido). Replace /path/to/ouraring-mcp with your checkout below.

Claude Code

claude mcp add ouraring \
  --env OURARING_REFRESH_TOKEN=... \
  --env OURARING_CLIENT_ID=... \
  --env OURARING_CLIENT_SECRET=... \
  -- node /path/to/ouraring-mcp/dist/index.js

Or per-project .mcp.json (user scope: claude mcp add -s user ...):

{
  "mcpServers": {
    "ouraring": {
      "command": "node",
      "args": ["/path/to/ouraring-mcp/dist/index.js"],
      "env": {
        "OURARING_REFRESH_TOKEN": "...",
        "OURARING_CLIENT_ID": "...",
        "OURARING_CLIENT_SECRET": "..."
      }
    }
  }
}

Codex CLI (~/.codex/config.toml)

[mcp_servers.ouraring]
command = "node"
args = ["/path/to/ouraring-mcp/dist/index.js"]

[mcp_servers.ouraring.env]
OURARING_REFRESH_TOKEN = "..."
OURARING_CLIENT_ID = "..."
OURARING_CLIENT_SECRET = "..."

OpenCode

opencode mcp add ouraring \
  --env OURARING_REFRESH_TOKEN=... \
  --env OURARING_CLIENT_ID=... \
  --env OURARING_CLIENT_SECRET=... \
  -- node /path/to/ouraring-mcp/dist/index.js

Config-file equivalent (opencode.jsonc; v2 nests under mcp.servers, older versions use flat mcp):

{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "servers": {
      "ouraring": {
        "type": "local",
        "command": ["node", "/path/to/ouraring-mcp/dist/index.js"],
        "environment": {
          "OURARING_REFRESH_TOKEN": "...",
          "OURARING_CLIENT_ID": "...",
          "OURARING_CLIENT_SECRET": "..."
        }
      }
    }
  }
}

Hermes (~/.hermes/config.yaml)

mcp_servers:
  ouraring:
    command: node
    args:
      - /home/hermes/github/ouraring-mcp/dist/index.js
    env:
      OURARING_REFRESH_TOKEN: "..."
      OURARING_CLIENT_ID: "..."
      OURARING_CLIENT_SECRET: "..."
    enabled: true

Then restart the gateway. Verify with oura_status (no network call).

OpenClaw

openclaw mcp add ouraring \
  --cwd /path/to/ouraring-mcp \
  -- command node --arg /path/to/ouraring-mcp/dist/index.js
openclaw mcp doctor ouraring --probe

Set the tokens via Settings → MCP (scoped config editor → environment values), or OURARING_REFRESH_TOKEN + client credentials in the gateway environment.

Claude Desktop (claude_desktop_config.json)

{
  "mcpServers": {
    "ouraring": {
      "command": "node",
      "args": ["/path/to/ouraring-mcp/dist/index.js"],
      "env": {
        "OURARING_REFRESH_TOKEN": "...",
        "OURARING_CLIENT_ID": "...",
        "OURARING_CLIENT_SECRET": "..."
      }
    }
  }
}

Examples

{ "data_type": "sleep", "start_date": "2026-10-15", "end_date": "2026-10-22" }
{ "collection": "heartrate", "start_datetime": "2026-10-22T00:00:00Z", "latest": true }
{ "collection": "ring_battery_level", "start_datetime": "2026-10-21T00:00:00-04:00" }
{ "collection": "workout", "start_date": "2026-10-01", "end_date": "2026-10-22", "fields": "activity_type,calories" }
{ "collection": "VO2_Max", "start_date": "2026-09-22", "end_date": "2026-10-22" }
{ "days": 7 }

Notes (checked against Oura's OpenAPI spec):

  • Collection names are case-insensitive, but the API path for VO₂max is case-sensitive upstream (vO2_max) — the server maps it automatically.

  • heartrate and ring_battery_level query by ISO datetime + latest; ring_configuration takes no date params; everything else takes start_date/end_date. Single-doc fetch via document_id only where the API offers it (not for enhanced_tag/heartrate/personal_info/ring_battery_level).

  • fields selects response fields on most routes (not daily_resilience, enhanced_tag, tag).

  • Webhooks (/v2/webhook/*) are out of scope: they need a public HTTPS URL and write operations — this server is read-only by design.

Privacy

See PRIVACY.md and TERMS.md: on-demand reads only, no sharing, single user. Revoke any time from your Oura dashboard.

Cold cache

Repeat date-range queries don't re-hit Oura: responses are cached in ./oura-cache.sqlite (node:sqlite, git-ignored — never committed) with per-day coverage. The last 2 days always refetch (Oura keeps finalizing them); older days are immutable — fetched once, served forever. Range queries that end in the past and are fully covered use zero upstream calls (cacheHit: true in the response). personal_info / ring_configuration are cached 30 days, single-document fetches 7 days. Any SQLite error disables the cache silently and the call goes straight to the API.

OURARING_CACHE_PATH=/tmp/oura.sqlite   # default ./oura-cache.sqlite
OURARING_CACHE_ENABLED=0               # bypass cache, fail-soft
OURARING_PERSONAL_INFO_TTL_HOURS=720   # default 30d
OURARING_RING_CONFIGURATION_TTL_HOURS=720
OURARING_CACHE_TTL_HOURS=168           # default 7d for the rest

(OURA_* aliases work for every knob above.)

Develop

npm test   # build + vitest (offline unit, stdio protocol, opt-in live)
npm audit --audit-level=moderate   # fail on moderate+ advisories (dev + prod deps)

tests/live.test.ts calls the real API only when OURARING_ACCESS_TOKEN is set; otherwise it passes with a skip note.

Every push is gated: npm run setup:hooks installs a pre-push hook that rebuilds and runs the suite — a red test blocks the push. GitHub Actions (.github/workflows/ci.yml) enforces the same on every push/PR, plus npm audit (moderate+) so new advisories break the build before merge. Dependency upgrades that fix audit findings must keep the suite green (e.g. vitest 3 → 5); if a fix needs a breaking major, do it in its own commit with the audit delta in the message.

Available Tools

6 tools
get_collectionA
Read-only

Read any Oura v2 collection: sleep sessions, workouts, guided sessions, tags, heartrate samples, sleep_time, rest_mode_period, ring_configuration, vo2_max, or daily_* summaries. One document via document_id, or a date range.

Inputs: collection (required), limit, fields, latest, +5 more.

get_dailyB
Read-only

Daily summaries for sleep, readiness, activity, stress, spo2, resilience or cardiovascular_age. Best for questions like 'how did I sleep this week?'.

Inputs: data_type (required), limit, fields, end_date, +1 more.

get_personal_infoA
Read-only

Your Oura profile (age, weight, height, email). Scopes: personal, email.

Inputs: no named parameters.

get_recent_summaryA
Read-only

One-call overview of the last N days (sleep + readiness + activity + stress + spo2 daily summaries) for natural-language summaries like 'how was my week?'.

Inputs: days.

list_collectionsA
Read-only

List every Oura collection this server can read, with the OAuth scope each needs.

Inputs: no named parameters.

oura_statusA
Read-only

Check whether Oura credentials are configured (no network call).

Inputs: no named parameters.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    A3.7/5.0

    Scored across 6 tools

    Disambiguation4/5

    Each tool has a reasonably distinct intent, and descriptions proactively guide selection (get_daily 'best for how did I sleep this week', get_recent_summary for a one-call week overview). The only real overlap is that get_collection's daily_* option and get_daily both surface daily summaries, plus get_recent_summary is effectively a composite of those, but the convenience layering is explained well enough to choose correctly.

    Naming Consistency4/5

    The surface follows a clear verb_noun pattern (list_collections, get_personal_info, get_daily, get_recent_summary, get_collection). oura_status is a minor deviation in style (server-prefixed noun_status), but it is still readable and unambiguous.

    Tool Count5/5

    Six tools is well-scoped: a status check, a discovery helper, a profile reader, three tiers of data reads (single-day, multi-day summary, generic collection). The generic get_collection tool absorbs breadth so no per-resource tool sprawl is needed.

    Completeness4/5

    Read coverage is broad and essentially complete via get_collection (sleep, workouts, tags, heartrate, ring config, vo2_max, daily_*), plus profile and credentials status. The gap is that the surface is strictly read-only — no create/update operations for tags, workouts, or personal info — which limits write-oriented workflows.

    Maintenance

    ActivityMaintained
    ResponsivenessNo recent community issues

    Related MCP Connectors

    Related MCP Servers

    • F
      license
      Not graded
      quality
      Not graded
      maintenance
      Enables AI assistants to access and analyze Oura Ring health data including sleep, readiness, activity, and stress metrics. Supports customizable queries, correlation analysis, and visualization capabilities for comprehensive health insights.
      -
    • A
      license
      Not graded
      quality
      D
      maintenance
      Enables AI assistants to access Oura Ring health data including sleep, readiness, activity, heart rate, and workouts through OAuth2-authenticated API calls with automatic token refresh and smart caching.
      8 npm
      MIT