Oura Ring MCP Server
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Oura Ring MCP ServerHow did I sleep last night?"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Oura Ring MCP Server
A self-hosted MCP server that exposes your Oura Ring data — daily activity, readiness, sleep, workouts, heart rate, stress, SpO2, sessions, and tags — to Claude (claude.ai connectors or Claude Code) over HTTP.
Change type: Feature. This is a fork of
camji55/oura-mcp at commit
c8db34f (2026-08-12),
MIT © 2026 Cameron Ingham (see LICENSE). Upstream's 494-line
server exists only as a Python string embedded in docker-compose.yml
(configs.oura_server_py.content) — nothing can import it, so it had zero
tests and no CI. This fork extracts it into an importable package with a
pytest suite, without changing behavior at the MCP tool boundary.
What changed vs. upstream
Package extraction. The inline server is now
src/oura_mcp/—config.py,client.py,auth.py,tools.py,server.py. All 11 tools keep identical names, signatures, field names, units, and docstrings.docker-compose.ymlmounts./srcand arequirements.lockinstead of embedding the server as a Composeconfig.Test harness. 42 tests: unit tests for pagination, field-mapping fidelity, and the auth accept/reject matrix, plus a 13-scenario integration suite at the real MCP protocol boundary (real JSON-RPC over HTTP through the real ASGI app, including the auth middleware). See Testing.
What this fork did not do: the four deployment security gaps this fork set out to check (loopback-only bind, constant-time token compare, fail-closed on missing
MCP_AUTH_TOKEN, pinned dependency lockfile) were already fixed upstream by commitc8db34f, two commits after the SHA originally targeted for this fork. This change verifies and test-locks that inherited hardening — see Security posture — it did not implement it from scratch.
Related MCP server: Whoop MCP Server
Tools
Tool | Description |
| Steps, calories, MET minutes by intensity, sedentary/resting time, activity score |
| Readiness score, temperature deviation from baseline, contributor scores (HRV balance, resting HR, etc.) |
| Daily sleep scores and contributors |
| Detailed sleep periods: bedtimes, stage durations, efficiency, avg HR/HRV, lowest HR |
| Logged workouts with type, intensity, calories, and start/end times |
| Compact multi-day summary with per-day rows and period averages |
| Per-day HR summaries from the intraday timeseries: min/avg/max bpm and averages by source |
| Time in high-stress and high-recovery zones, plus Oura's day classification |
| Nightly average SpO2 and breathing disturbance index |
| Meditation, breathing, nap, and relaxation sessions with type, mood, and times |
| User-entered tags and notes (illness, travel, alcohol, custom) |
Date-range tools default to the last 7 days when called without arguments. Fields are raw Oura values with explicit unit suffixes and full ISO 8601 timestamps — never human-formatted durations or times.
Requirements
Docker with Compose v2 (to run the server)
uv(to run tests / develop locally)An Oura account with a personal access token
Quick start
Clone the repo and create your
.env:cp .env.example .envEdit
.env:OURA_ACCESS_TOKEN— your personal access token from cloud.ouraring.com/personal-access-tokensMCP_AUTH_TOKEN— a long random secret that gates access to the server. Generate one:openssl rand -hex 32
Start it:
docker compose up -dFirst start takes ~30s while pip installs dependencies from
requirements.lock(cached in a volume afterwards). The server listens on127.0.0.1:8000; override withOURA_MCP_PORT/OURA_MCP_BINDin.env.Check health:
curl http://localhost:8000/health{"status": "ok"}means the server is up. To also verify your Oura token works, authenticate the same endpoint:source .env && curl -H "Authorization: Bearer $MCP_AUTH_TOKEN" http://localhost:8000/health{"status": "ok", "oura_api": true}means the server can reach the Oura API with your token.
Connecting Claude
claude.ai (custom connector): add a connector with the URL
https://<your-host>/mcp/<MCP_AUTH_TOKEN>Claude Code:
claude mcp add --transport http oura https://<your-host>/mcp --header "Authorization: Bearer <MCP_AUTH_TOKEN>"Configuration
All configuration is via environment variables, loaded from .env by Docker
Compose:
Variable | Required | Description |
| yes | Oura personal access token |
| yes | Secret gating all |
| no | Oura API request timeout in seconds (default |
| no | Host port the server is published on (default |
| no | Host interface to bind (default |
Compose fails fast with a clear error if either required variable is
missing; the server itself also refuses to start (RuntimeError) if
MCP_AUTH_TOKEN is unset, whether run under Compose or via
uvicorn oura_mcp.server:app directly.
Testing
uv sync
uv run pytestRuns in CI on every push and PR to main via
.github/workflows/test.yml (uv sync --locked && uv run pytest,
Python 3.12 — matching the container's python:3.12-slim). The docker-marked
container smoke test is excluded from the default run (see below) and does
not run in CI.
42 tests, ~99% coverage on client.py/tools.py (gate held at 98%). Every
Oura API call is stubbed from synthetic fixtures in tests/fixtures/
(generated against Oura's public OpenAPI spec, vendored as
openapi-1.37.json — no real health data, no live PAT). An autouse fixture
wraps every test in respx's network guard, which raises immediately on any
unmocked HTTP call rather than letting it reach the network.
tests/test_client.py—OuraClientpagination, including the two-page concatenation test (the single highest-value test in the suite).tests/test_tools.py— per-tool field-mapping fidelity, including a golden-record check forget_sleep_periods.tests/test_auth.py— the auth middleware accept/reject matrix.tests/test_fixtures_validate.py— fixtures validated against the vendored OpenAPI spec (scripts/validate_fixtures.py).tests/test_integration.py— 13 end-to-end scenarios at the real MCP protocol boundary (JSON-RPC over HTTP through the real ASGI app and auth middleware): handshake, single- and multi-page tool calls, unit/timestamp fidelity, heart-rate summarization, the full auth matrix including fail-closed-on-unset-token, health endpoint behavior, and explicit error propagation on upstream 401/429/timeout (no silent failures).
The container smoke test (test_container_smoke_docker_compose_up: real
docker compose up, poll /health, one real initialize against
127.0.0.1:8000, and a check that the published port is loopback-only) is
marked @pytest.mark.docker + @pytest.mark.slow and skipped by default —
run it explicitly with uv run pytest -m docker. Docker isn't installed
on the machine this fork is normally developed on, so this test can't run
there; it instead runs on demand in CI via the docker-smoke job in
.github/workflows/test.yml
(workflow_dispatch, since GitHub-hosted runners ship Docker) —
verified passing.
Security posture
All of the following were already present in upstream at the c8db34f fork
point — this fork verifies them with tests and preserves them through the
package extraction, it did not introduce them:
Loopback-only by default.
docker-compose.ymlbinds127.0.0.1:8000:8000unless you setOURA_MCP_BIND=0.0.0.0.Fail-closed auth. The server refuses to start if
MCP_AUTH_TOKENis unset —RuntimeErrorat import time, so this holds underuvicorndirectly, not just under Compose's${VAR:?}guard.Constant-time token comparison (
hmac.compare_digest), so response timing leaks nothing about the token.Pinned dependencies.
requirements.lockis fully pinned (viauv pip compile); the container installs from it at start rather than resolving version ranges fresh each time.Auth-gated health detail.
/healthanswers anonymous callers with bare liveness ({"status": "ok"}) only. Oura connectivity detail — which would reveal whether your token is currently valid — requires the auth token, and the upstream check behind it is cached 60s so it can't be used to burn your Oura API quota.Container hardening carried over unchanged:
no-new-privileges, 256 MB memory limit, log rotation.
Still the operator's responsibility: TLS termination. The auth token
travels in the URL path (for claude.ai connectors) or a header — put the
server behind a TLS-terminating reverse proxy (Caddy, nginx, Cloudflare
Tunnel, Tailscale) before exposing it beyond localhost. Path-based tokens
can end up in proxy access logs — treat those logs as sensitive. Keep .env
out of version control (already covered by .gitignore); if a token leaks,
revoke it at cloud.ouraring.com and generate a new MCP_AUTH_TOKEN.
Known gaps / follow-ups
health()blocks the event loop. The handler isasync defbut calls synchronoushttpxunder the hood viaOuraClient— a pre-existing upstream characteristic, carried over unchanged rather than fixed inline, per this fork's behavior-preserving-extraction scope. Worth a dedicated follow-up if/healthlatency ever matters under load.
How it works
docker-compose.yml starts a stock python:3.12-slim container, bind-mounts
src/oura_mcp and requirements.lock, installs dependencies from the
lockfile at boot, and runs uvicorn oura_mcp.server:app. Auth is a small
Starlette middleware (oura_mcp.auth.TokenPathAuthMiddleware) that accepts
either POST /mcp/<token> (claude.ai) or POST /mcp with a Bearer header
(Claude Code), and OuraClient transparently follows next_token
pagination to exhaustion on every collection endpoint.
License
MIT © 2026 Cameron Ingham (upstream). Fork point: c8db34f.
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Connectors
Multi-tenant hosted MCP server for Oura Ring — 21 read-only tools, OAuth per user.
Private Apple Health metrics and workout detail for ChatGPT, Claude, and any MCP client.
WHOOP recovery, strain, sleep and workouts in Claude via official WHOOP OAuth. Free, open source.
Pace is a remote MCP server that exposes wearable and fitness data to Claude via the Model Context Protocol. It connects to Garmin, Oura, Whoop, Polar, Fitbit and 20+ devices and provides 15 tools for querying sleep, activity, recovery, and training data. Hosted on Google Cloud Run, OAuth 2.1 authentication, Streamable HTTP transport. Instructions: First you need to create an account at: https://pacetraining.co and connect your wearables. After that you can connect the remote Server via Custom Connector in Claude and OAuth 2.1 Flow startet.
Related MCP Servers
- AlicenseBqualityFmaintenanceEnables querying sleep, readiness, and resilience data from the Oura API via the Model Context Protocol (MCP), allowing natural language interaction and seamless integration with tools like Claude.638MIT
- AlicenseNot gradedqualityCmaintenanceConnects Whoop health data to Claude via an MCP server, enabling retrieval of recovery, sleep, strain, and workout metrics through natural language tools.163MIT
- AlicenseNot gradedqualityDmaintenanceEnables Claude to access and query Oura Ring health data including sleep, activity, readiness, heart rate, and more via the Oura API.MIT
- FlicenseNot gradedqualityCmaintenanceEnables Claude and other MCP clients to access Oura Ring data, including daily activity, sleep, readiness, stress, and more, via a remote Streamable HTTP server.18
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
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/ihadanidea/oura-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server