The Oura v2 API as an MCP server. Paginates, fixes the date range, warns when data is missing.
- ✓Open-source license (MIT)
- ✓Actively maintained (<30d)
- ✓Clear description
- ✓Documented (README)
claude mcp add oura-mcp -- uvx oura-mcp{
"mcpServers": {
"oura-mcp": {
"command": "uvx",
"args": ["oura-mcp"]
}
}
}Resumen de MCP Servers
# oura-mcp
[](https://pypi.org/project/mcp-oura/)
[](https://glama.ai/mcp/servers/proscar87/oura-mcp)
[](LICENSE)
English | [简体中文](https://github.com/proscar87/oura-mcp/blob/main/README_zh_CN.md) | [한국어](https://github.com/proscar87/oura-mcp/blob/main/README_ko.md) | [Español](https://github.com/proscar87/oura-mcp/blob/main/README_es.md)
<!-- The Glama badge is live, not a screenshot: it renders whatever that
independent index scores this server at today. A badge that can only go up
is decoration; one that can drop is evidence. -->
The [Oura](https://ouraring.com) v2 API as an [MCP](https://modelcontextprotocol.io)
server. All 19 collections, three tools, no dependencies beyond the MCP SDK.
### One local day of heart rate is 1,231 samples across 2 pages
A client that doesn't follow Oura's `next_token` returns **1,000 of them — 81%,
looking complete, with nothing saying otherwise.** Measured against the real API
on 9 August 2026, one person, one ring, 24 hours.
**The test to apply to any Oura MCP server, including this one:** does it take
`next_token` — or a `cursor`, or a `limit` — as a tool parameter? If it does,
pagination is the model's job, and a model that forgets to ask again produces a
confident answer off partial data. This server paginates to exhaustion before it
returns, and tells you how many pages it took.
That's one of **four** ways Oura under-delivers without saying so. All four are
measured below, and all four are corrected here.
### Install
Download **`oura-mcp.mcpb`** from the
[latest release](https://github.com/proscar87/oura-mcp/releases/latest) and
double-click it. Claude Desktop does the rest — no terminal, no Python, no Node.
It runs on Oura's official sample data out of the box, and every sample response
says so, so nothing can pass for your own sleep.
Prefer the command line? `uvx --from mcp-oura oura-mcp`.
Prefer no Python on your machine at all?
```
docker run -i --rm -e OURA_SANDBOX=1 ghcr.io/proscar87/oura-mcp
```
`-i` is not optional: an MCP server speaks over stdin and stdout, not over a
port. Without it the container has no stdin, the handshake never arrives, and
the client reports a server that doesn't show up.
---
## The problem, measured
Oura does not return errors when it can't give you what you asked for. It
returns something different, shaped like a correct response. These are the four
we found by measuring against the real API on 9 August 2026:
### 1. Skip the pagination and you get a fraction
```json
{ "data": [ ... ], "next_token": "eyJ0eXAiOi..." }
```
If `next_token` comes back and you don't follow it, you receive the first page
and **nothing warns you**. One local day of `heartrate` — one person, one ring,
24 hours — is **1,231 samples across 2 pages**. A client that doesn't paginate
gets 1,000 of 1,231: 81%, looking complete. A month is ~37,000.
### 2. Asking for a single day returned zero records
`end_date` **does not behave the same across collections**:
| Exclude the last day requested | Include it |
|---|---|
| `daily_activity`, `sleep`, `workout` | `daily_sleep`, `daily_readiness`, `daily_stress`, `daily_spo2`, `daily_resilience`, `daily_cardiovascular_age`, `sleep_time` |
And on top of that, **`workout` filters by UTC date while reporting `day` in
local time**: at `-06:00`, asking for July 16–18 returned records from the 15th
and 16th — *before* the requested start.
Here the range is inclusive on both ends, always. Two extra days are requested
on each side and then trimmed, which is correct whichever way a given collection
behaves — and stays correct when Oura changes it.
### 3. `latest=true` is ignored where it doesn't apply
Only `heartrate` and `ring_battery_level` honor it. In the other seventeen Oura
doesn't error: it **returns the entire collection**. You ask for the latest
record, you get ten, and you believe it's one. Here it's rejected before the
request goes out.
### 4. A field that doesn't exist is silently ignored
`fields=does_not_exist` returns the **complete** record — the projection never
happens — and `fields=score,does_not_exist` applies the good one and drops the
bad one without a word. Here, fields that never appeared are reported under
`ignored_fields`.
**The pattern is always the same:** you ask for one thing, you get another, and
nothing warns you. That's why this package would rather shout than quietly
under-deliver.
## Installation
### Try it with no credentials
```bash
pip install mcp-oura
OURA_SANDBOX=1 oura-mcp --check
```
The sandbox is official — it's in Oura's OpenAPI spec, with 34 mirror routes —
and serves synthetic data without authentication. 18 of the 19 collections work
there: `personal_info` doesn't, which makes sense, since it's the one returning
email, age, weight and height.
This is the right order: first you watch the server work and learn the shape of
the data, then you go get credentials.
### With your own data
**Oura stopped issuing Personal Access Tokens in December 2025.** Existing ones
still work; new ones can't be created. So there are two paths:
**a) OAuth2 — the one that works today.** Register an application at
[cloud.ouraring.com/oauth/applications](https://cloud.ouraring.com/oauth/applications)
with the redirect `http://localhost:9876/callback/` — **the trailing slash is
required**, the portal rejects the other form with `invalid_redirect_uri`.
> **If you registered on `developer.ouraring.com` instead**, your app belongs to
> Oura's newer portal, whose token endpoint is a different one. The legacy
> endpoint rejects those apps on **every** refresh — so the registration works
> exactly once, until the first access token expires, and then fails forever
> with nothing explaining why. This server tries the legacy endpoint and falls
> back to the new one automatically; nothing to configure either way.
```bash
export OURA_CLIENT_ID="…"
export OURA_CLIENT_SECRET="…"
oura-mcp --authorize # opens the browser, waits for the callback
oura-mcp --authorize --manual # headless machines: you paste the URL back
```
The token is stored in `~/.config/oura-mcp/credenciales.json` with mode 600 — or
in the system keychain if you happen to have `keyring` installed, which is not a
dependency of this package — and refreshes itself. `oura-mcp --forget` erases
it.
**b) A personal token, if you already had one.**
```bash
export OURA_PAT="your-token"
oura-mcp --check
```
`--check` is the self-check: it reports which credential you're using, which
scopes were granted and how long the access has left, **without returning the
token or a single health value**. It reports the token's length, never the
token. Error messages get copied and pasted into chats and issues; they have no
business carrying anything else.
### Connecting it to Claude Code
With the package installed (`pip install mcp-oura`):
```bash
claude mcp add -s user oura --env OURA_SANDBOX=1 -- oura-mcp
```
Drop `OURA_SANDBOX` once you've run `oura-mcp --authorize`.
**If you use [uv](https://docs.astral.sh/uv/)**, nothing needs to be installed
permanently:
```bash
claude mcp add -s user oura --env OURA_SANDBOX=1 -- uvx --from mcp-oura oura-mcp
```
The `--from` is required because the distribution is named `mcp-oura` and the
executable `oura-mcp`. *(This needs `uv`; without it the command above fails
with "command not found", and `pip install` is the path to take.)*
As a Claude Code plugin:
```bash
claude plugin marketplace add proscar87/oura-mcp
claude plugin install oura@oura-mcp
```
### Connecting it to Claude Desktop
**One click:** download `oura-mcp.mcpb` from the
[releases page](https://github.com/proscar87/oura-mcp/releases) and double-click
it. Claude Desktop installs it — no terminal, no JSON, no Python. It ships with
sample data turned on, so it works before you have any credential at all.
When you want your own data, just ask it for something: it opens Oura's
authorization page through Claude, waits for the callback, and retries what you
asked. No terminal. That works because MCP has a mode for precisely this — URL
elicitation — and the client does the opening.
The one thing Oura still requires is that every application be registered, so you
need a client ID and secret from
[cloud.ouraring.com/oauth/applications](https://cloud.ouraring.com/oauth/applications)
once. That's Oura's rule, not this server's. `oura-mcp --authorize` remains for
terminal users and for clients that can't show a URL.
**Or by hand,** in `~/Library/Application Support/Claude/claude_desktop_config.json`:
```json
{
"mcpServers": {
"oura": {
"command": "/full/path/to/oura-mcp",
"env": { "OURA_SANDBOX": "1" }
}
}
}
```
`which oura-mcp` gives you the full path. Claude Desktop does not inherit your
terminal's `PATH`, so a bare name there fails silently — one of the most common
mistakes when configuring an MCP server.
## The tools
| | |
|---|---|
| `oura_collections` | All 19, what each one carries and which parameters it takes |
| `oura_query` | One collection in full over a range, paginating to the end |
| `oura_check` | Self-check that exposes nothing |
**Three, not nineteen.** A server with one tool per collection forces the model
to pick among 19 similar names before knowing what any of them contain. Here the
collection is a parameter and the catalog is consulted when needed.
All three declare themselves read-only, and that isn't a promise: there is no
`POST`, `PUT` or `DELETE` anywhere in the package, and a test reads the source to
keep it that way.
### `oura_query` parameters
| | |
|---|---|
| `collection` | Which of the 19. `oura_collections` lists them |
| `day` | A single day. Shorthand for `start=end=day` |
| `start`, `end` | The range, **inclusive on both ends** |
| `fields` | OnlLo que la gente pregunta sobre oura-mcp
¿Qué es proscar87/oura-mcp?
+
proscar87/oura-mcp es mcp servers para el ecosistema de Claude AI. The Oura v2 API as an MCP server. Paginates, fixes the date range, warns when data is missing. Tiene 0 estrellas en GitHub y su última actualización registrada es del 2026-09-08.
¿Cómo se instala oura-mcp?
+
Puedes instalar oura-mcp clonando el repositorio (https://github.com/proscar87/oura-mcp) o siguiendo las instrucciones del README en GitHub. ClaudeWave también te ofrece bloques de instalación rápida en esta misma página.
¿Es seguro usar proscar87/oura-mcp?
+
Nuestro agente de seguridad ha analizado proscar87/oura-mcp y le ha asignado un Trust Score de 87/100 (tier: Trusted). Revisa el desglose completo de comprobaciones superadas y flags en esta página.
¿Quién mantiene proscar87/oura-mcp?
+
proscar87/oura-mcp es mantenido por proscar87. La última actividad registrada en GitHub es del 2026-09-08, con 0 issues abiertos.
¿Hay alternativas a oura-mcp?
+
Sí. En ClaudeWave puedes explorar mcp servers similares en /categories/mcp, ordenados por popularidad o actividad reciente.
Despliega oura-mcp en tu cloud
Lleva este repo a producción en minutos. Cada plataforma genera su propio entorno con variables de entorno editables.
¿Mantienes este repo? Añade un badge a tu README
Pega el badge en tu README de GitHub para mostrar que está auditado por ClaudeWave. Cada badge enlaza de vuelta a esta página y muestra el Trust Score actual.
[](https://claudewave.com/repo/proscar87-oura-mcp)<a href="https://claudewave.com/repo/proscar87-oura-mcp"><img src="https://claudewave.com/api/badge/proscar87-oura-mcp" alt="Featured on ClaudeWave: proscar87/oura-mcp" width="320" height="64" /></a>Más MCP Servers
Fair-code workflow automation platform with native AI capabilities. Combine visual building with custom code, self-host or cloud, 400+ integrations.
User-friendly AI Interface (Supports Ollama, OpenAI API, ...)
An open-source AI agent that brings the power of Gemini directly into your terminal.
Real-time global intelligence dashboard. AI-powered news aggregation, geopolitical monitoring, and infrastructure tracking in a unified situational awareness interface
The fastest path to AI-powered full stack observability, even for lean teams.
🕷️ An adaptive Web Scraping framework that handles everything from a single request to a full-scale crawl!