jaredstauffer

Oura MCP Server

Community jaredstauffer
Updated

MCP server for Oura Ring data — stdio for local clients, HTTP + OAuth for claude.ai and mobile

Oura MCP Server

A Model Context Protocol (MCP) server for accessing Oura Ring data.

It runs in two modes from the same codebase:

Mode Entrypoint Transport Use it for
Local npm run start:stdio stdio Claude Code and Claude Desktop on your own machine
Hosted npm start Streamable HTTP + OAuth claude.ai in the browser and the Claude mobile apps

Prerequisites

  • Node.js 20+
  • An Oura account

Installation

npm install
npm run build

Oura credentials

  1. Log in to the Oura Cloud Console
  2. Create a Personal Access Token

Set it as OURA_PERSONAL_ACCESS_TOKEN. See .env.example for the full list ofvariables. Set OURA_TIMEZONE to your IANA zone (e.g. America/Phoenix) — itdecides what "the last 7 days" means for the resource defaults. It falls back toUTC, which shifts the window by a day for part of every day anywhere west ofGreenwich, so it is worth setting explicitly.

Personal access tokens are the only supported credential. Earlier versions alsoaccepted OAuth2 client id/secret, but nothing ever ran the authorization-codeflow against Oura, so that path could only fail at request time; it has beenremoved rather than left as a trap.

Local mode (stdio)

Claude Code

claude mcp add oura -s user \
  -e OURA_PERSONAL_ACCESS_TOKEN=your_token \
  -- "$(command -v node)" /absolute/path/to/oura-mcp/build/index.js

Claude Desktop

Settings → Developer → Edit Config:

{
  "mcpServers": {
    "oura": {
      "command": "/absolute/path/to/node",
      "args": ["/absolute/path/to/oura-mcp/build/index.js"],
      "env": { "OURA_PERSONAL_ACCESS_TOKEN": "your_token" }
    }
  }
}

Pass the token in env rather than relying on a .env file. dotenv resolves.env against the current working directory, which for a client-launchedserver is wherever the client happened to start — not this repo.

Testing

npm test        # builds first, then runs the suite

22 tests across two suites, none of which need a live Oura token: oauth.test.tsdrives the real OAuth flow over HTTP (discovery, dynamic registration, PKCE,single-use codes, refresh, bearer rejection), and tools.test.ts checks the tooland resource surface over stdio — including a regression test that stdout carriesnothing but JSON-RPC.

For a manual probe against real data:

node test.js get_daily_sleep 2026-08-01

Hosted mode (HTTP + OAuth)

claude.ai and the mobile apps only talk to remote MCP servers over HTTPS, soreaching your data from a phone means deploying this somewhere.

What the auth actually does

The server is its own OAuth 2.1 authorization server. Your Oura token stays inserver-side env and is never handed to the client; the OAuth flow exists only toprove that whoever is calling /mcp knows MCP_AUTH_PASSWORD.

Client ids, authorization codes, and tokens are all HMAC-signed payloads ratherthan database rows, so a redeploy doesn't sign you out and no storage needsprovisioning. Clients are registered as public clients and authenticate withPKCE. Authorization codes are single-use.

Deploying to Railway

  1. Create a new Railway project from this repo. railway.json pins the buildand start commands and points the healthcheck at /healthz.

  2. Generate a signing secret:

    openssl rand -hex 32
    
  3. Set these variables in the Railway service:

    Variable Value
    OURA_PERSONAL_ACCESS_TOKEN your Oura token
    OAUTH_SIGNING_SECRET the hex string from step 2
    MCP_AUTH_PASSWORD the password you'll type when connecting

    You do not need to set PUBLIC_URL on Railway. The server falls back toRAILWAY_PUBLIC_DOMAIN, which Railway injects once the service has a domain(Settings → Networking → Public Networking → Generate Domain). SetPUBLIC_URL explicitly only when hosting elsewhere, or to override theadvertised origin — it must then match the real origin exactly, since it'swhat the server publishes in its OAuth metadata.

  4. Confirm the deploy: curl https://your-app.up.railway.app/healthz

PORT is injected by Railway; don't set it yourself.

Connecting Claude

In claude.ai → Settings → Connectors → Add custom connector, use:

https://your-app.up.railway.app/mcp

Leave the OAuth client fields blank — the server supports dynamic clientregistration, so Claude registers itself. You'll be redirected to a sign-in pageasking for MCP_AUTH_PASSWORD, and after that the connector is available in thebrowser and on the mobile apps under the same account.

Endpoints

Path Purpose
POST /mcp The MCP endpoint. Requires a bearer token and the oura:read scope.
/authorize, /token, /register, /revoke OAuth, mounted by the MCP SDK
POST /login Password form posted from the authorize page
/.well-known/oauth-authorization-server AS metadata
/.well-known/oauth-protected-resource/mcp Protected-resource metadata
GET /healthz Healthcheck

GET and DELETE on /mcp return 405: the server runs the transport instateless mode, so there's no long-lived SSE stream or session to tear down.Every request gets a fresh server instance, which is what lets a redeploy or asecond replica pick up mid-conversation.

Available resources

personal_info, daily_activity, daily_readiness, daily_sleep, sleep,sleep_time, workout, session, daily_spo2, rest_mode_period,ring_configuration, daily_stress, daily_resilience,daily_cardiovascular_age, vO2_max

Date-based resources default to the last 7 days, bounded by OURA_TIMEZONE.

Response shape

Results are paginated by Oura via next_token; the server follows it tocompletion, so a wide date range returns every record rather than the first page.It stops after 25 pages and marks the response truncated rather than looping.

Interval-sample fields — the per-30-second and per-5-minute arrays on sleep(heart_rate, hrv, movement_30_sec, sleep_phase_5_min) anddaily_activity (class_5_min, met) — are stripped by default, since a monthof them runs to megabytes and crowds out the conversation. PassincludeIntervalSamples: true on a narrow range when you need them.

Available tools

Every date-based resource above has a matching get_<name> tool takingstartDate and endDate in YYYY-MM-DD form — 13 in total.

MCP Server · Populars

MCP Server · New

    Get-Concord-AI

    Concord MCP

    Live messaging for coding agents

    Community Get-Concord-AI
    alijancb

    Subio MCP

    Open-source MCP server for discovering fast-growing internet conversations with Subio

    Community alijancb
    ruezo

    MCP Video Digest (视频内容提取总结)

    MCP Server for transcribing videos via video links and summarizing video content

    Community ruezo
    LastSearch-HQ

    LastSearch

    Reliable research infrastructure for AI agents. Evidence-backed web search with citations, confidence scores, and Clarity anti-hallucination. MCP server, REST API, Python SDK.

    Community LastSearch-HQ
    gtfodevs

    Autonomo MCP

    Tired of 'it works' lies? Autonomo MCP makes your AI prove it—on real hardware, right in your editor.

    Community gtfodevs