# oura-mcp [![CI](https://github.com/YasuakiOmokawa/oura-mcp/actions/workflows/ci.yml/badge.svg)](https://github.com/YasuakiOmokawa/oura-mcp/actions/workflows/ci.yml) [![npm](https://img.shields.io/npm/v/@yasuakiomokawa/oura-mcp.svg)](https://www.npmjs.com/package/@yasuakiomokawa/oura-mcp) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE) A Model Context Protocol server for the [Oura Ring API v2](https://cloud.ouraring.com/v2/docs). Exposes sleep, activity, readiness, heart rate, and workout data to MCP-compatible clients (Claude Desktop, Claude Code, Cursor, ...) via OAuth. ## Quick start ```bash npx @yasuakiomokawa/oura-mcp configure ``` The wizard collects your Oura Client ID/Secret, walks through browser OAuth, saves tokens to `~/.config/oura-mcp/`, and adds an `mcpServers.oura` entry to any detected MCP client config. Restart the client and the tools below are available. Re-running `configure` pre-fills the saved Client ID / port so you only need to press Enter to keep them. Type `--force` to wipe saved state and start from scratch: ```bash npx @yasuakiomokawa/oura-mcp configure --force ``` ## Prerequisites 1. Register an Oura developer app at https://cloud.ouraring.com/oauth/applications 2. **Redirect URI must be exactly**: `http://localhost:54321/callback` (or `http://localhost:/callback` if you customize `OURA_CALLBACK_PORT`) 3. Enable the read scopes you need (Email, Personal info, Daily activity, Heart rate, Workout, Tag, Session, SpO2, Ring configuration, Stress, Heart health) 4. Note the Client ID and Client Secret — you'll enter them in `npx @yasuakiomokawa/oura-mcp configure` ## Installation Three paths depending on your client: **1. MCP Registry (auto-discovery clients)** Once published to the [official MCP Registry](https://github.com/modelcontextprotocol/registry), supported clients can install `io.github.YasuakiOmokawa/oura-mcp` from their UI. The wizard step still has to run once to obtain OAuth tokens. **2. Manual config (Claude Desktop / Claude Code / Cursor)** Run `npx @yasuakiomokawa/oura-mcp configure` — Step 4 of the wizard auto-detects: - Claude Desktop: `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) or `%APPDATA%\Claude\claude_desktop_config.json` (Windows) - Claude Code (user): `~/.claude.json` - Claude Code (project): `./.mcp.json` - Cursor (user): `~/.cursor/mcp.json` - Cursor (project): `./.cursor/mcp.json` Each detected file is backed up to `.bak.` before an atomic write. To configure manually, add to your client config: ```json { "mcpServers": { "oura": { "command": "npx", "args": ["-y", "@yasuakiomokawa/oura-mcp"] } } } ``` **3. Skill (optional)** The companion [`oura-api-skill`](skills/oura-api-skill/SKILL.md) ships per-endpoint reference and three workflow recipes (weekly review / sleep trend / recovery check). Bundle it as a Claude Code plugin or import into your skills directory. ## Tools provided | Tool | Purpose | |---|---| | `oura_authenticate` | Start OAuth flow in browser; returns the URL. Used after `refresh_token` expires. | | `oura_auth_status` | Check current token validity and expiry. | | `oura_clear_auth` | Wipe stored tokens. | | `oura_api_list_paths` | List every supported `GET` endpoint with summaries. | | `oura_api_get` | Generic `GET` to `/v2/...`. Auto-paginates via `max_pages` (1-20) or accepts `next_token` in `params`. | `oura_api_get` returns `structuredContent` with `{ status, data, next_token, pages_fetched, has_more }`. ## Configuration Two ways. **The config file (Option A) is the recommended path** — it stores secrets at-rest with `0600` and is self-healing. Environment variables (Option B) are kept for CI / Docker / ephemeral environments where writing a file is impractical, but they leak more easily and are not recommended for daily use. ### Option A — config file (recommended) Run the wizard once and forget about it: ```bash npx @yasuakiomokawa/oura-mcp configure ``` This writes: ``` ~/.config/oura-mcp/config.json # 0600, contains clientId / clientSecret / callbackPort ~/.config/oura-mcp/tokens.json # 0600, contains the OAuth access / refresh tokens ``` Permissions are re-checked on every load and chmod'd back to `0600` if anything else touched them. `config.json` schema: ```json { "schemaVersion": 1, "clientId": "...", "clientSecret": "...", "callbackPort": 54321 } ``` ### Option B — environment variables (CI / Docker only) ```bash OURA_CLIENT_ID=... OURA_CLIENT_SECRET=... # must be set together with OURA_CLIENT_ID OURA_CALLBACK_PORT=54321 # optional; safe to set in env regardless of Option A/B ``` When the server boots and both `OURA_CLIENT_ID` and `OURA_CLIENT_SECRET` are set, it uses them and emits a `config.env_credentials` warning to stderr. **Why not recommended**: - `process.env` is readable from `/proc//environ` by any process running as the same user. - Environment is inherited by every child process the server spawns. - Crash dumps and observability tools that capture `process.env` will leak the secret. - `OURA_CLIENT_SECRET=... npx ...` typed at the shell ends up in shell history. `OURA_CALLBACK_PORT` is **not** a secret and is fine to pass via env in either mode. ### Never use `args` for secrets Process arguments are visible to other users via `ps` / `/proc//cmdline`. Use the `env` block of your MCP client config: ```json { "mcpServers": { "oura": { "command": "npx", "args": ["-y", "@yasuakiomokawa/oura-mcp"], "env": { "OURA_CLIENT_ID": "...", "OURA_CLIENT_SECRET": "..." } } } } ``` ## Troubleshooting - **"refresh_token expired"** — run `oura_authenticate` (in chat) or `npx @yasuakiomokawa/oura-mcp configure` (in terminal). - **Port 54321 already in use** — set `OURA_CALLBACK_PORT=` and update the redirect URI in your Oura developer app to match. - **"Path not found"** — verify the path with `oura_api_list_paths`. Common slips: missing `/v2/` prefix, typo in `daily_sleep`. - **Setup hangs at "Waiting for authorization"** — you haven't approved in the browser yet, or the authorize page was opened in a different browser session than the one with localhost reachability. - **No log output** — set `OURA_LOG_LEVEL=debug` for verbose stderr logging. ## Development ```bash git clone https://github.com/YasuakiOmokawa/oura-mcp.git cd oura-mcp npm install npm test npm run build ``` Useful scripts: - `npm run lint` / `npm run typecheck` — Biome + TypeScript checks - `npm run test:coverage` — Vitest with V8 coverage - `npm run update:docs` — re-fetch the Oura OpenAPI schema and regenerate `skills/oura-api-skill/references/` ## License [MIT](LICENSE)