ultrahuman_mcp
# π Ultrahuman Ring MCP Server
A [Model Context Protocol (MCP)](https://modelcontextprotocol.io) server that exposes real-time health data from the **Ultrahuman Ring** to AI agents, LLMs, and agentic workflows. The server connects to the Ultrahuman Partner API, processes raw biometric data, and returns **agent-optimized summaries** with trend analysis, quality classifications, and actionable insights.
---
## Table of Contents
- [Architecture Overview](#architecture-overview)
- [Prerequisites](#prerequisites)
- [Quick Start](#quick-start)
- [MCP Client Configuration](#mcp-client-configuration)
- [Common Parameters](#common-parameters)
- [Tools Reference](#tools-reference)
- [`get_health_summary`](#1-get_health_summary)
- [`get_heart_vitals`](#2-get_heart_vitals)
- [`get_sleep_analysis`](#3-get_sleep_analysis)
- [`get_activity_data`](#4-get_activity_data)
- [`get_recovery_score`](#5-get_recovery_score)
- [`get_temperature_data`](#6-get_temperature_data)
- [`get_daily_metrics`](#7-get_daily_metrics)
- [Tool Selection Guide](#tool-selection-guide)
- [Response Conventions](#response-conventions)
- [Scoring & Classification Thresholds](#scoring--classification-thresholds)
- [Example Agent Conversations](#example-agent-conversations)
- [Project Structure](#project-structure)
- [License](#license)
---
## Architecture Overview
```
βββββββββββββββββββββββ stdio ββββββββββββββββββββββββββββ HTTPS ββββββββββββββββββββββββ
β LLM / AI Agent βββββββββββββββββββββΊβ Ultrahuman MCP Server βββββββββββββββββββββΊβ Ultrahuman API β
β (Claude, GPT, etc) β MCP Protocol β (Node.js / TypeScript) β Partner API v1 β partner.ultrahuman β
βββββββββββββββββββββββ ββββββββββββββββββββββββββββ ββββββββββββββββββββββββ
β
βΌ
ββββββββββββββββββββββββ
β Smart Summarization β
β β’ Time filtering β
β β’ Trend analysis β
β β’ Quality scoring β
β β’ Insight generationβ
ββββββββββββββββββββββββ
```
The server sits between the LLM agent and the raw Ultrahuman API. It performs:
1. **Authentication** β Injects the API key on every request.
2. **Smart Summarization** β Converts raw time-series arrays into aggregated stats (avg, min, max, trend, count).
3. **Time Filtering** β Supports `last_minutes`, `start_time`/`end_time` windows, and date selection.
4. **Quality Classification** β Maps numeric scores to human-readable labels (`excellent`, `good`, `fair`, `poor`).
5. **Insight Generation** β Produces contextual health insights from cross-metric analysis.
---
## Prerequisites
| Requirement | Details |
|---|---|
| **Node.js** | v18 or higher |
| **Ultrahuman Ring** | With active subscription and data synced |
| **API Key** | Obtain from [Ultrahuman Vision](https://vision.ultrahuman.com) |
---
## Quick Start
### 1. Install Dependencies
```bash
npm install
```
### 2. Configure API Key
```bash
cp .env.example .env
```
Edit `.env`:
```env
ULTRAHUMAN_API_KEY=your_api_key_here
```
### 3. Build & Start
```bash
npm run build
npm start
```
The server communicates over **stdio** (standard input/output) using the MCP protocol.
---
## MCP Client Configuration
### Claude Desktop
Add to `~/Library/Application Support/Claude/claude_desktop_config.json`:
```json
{
"mcpServers": {
"ultrahuman": {
"command": "node",
"args": ["/absolute/path/to/ultrahuman_mcp/dist/index.js"],
"env": {
"ULTRAHUMAN_API_KEY": "your_api_key_here"
}
}
}
}
```
### Cursor / Windsurf / Other MCP Clients
Configure with:
- **Command**: `node`
- **Args**: `["/absolute/path/to/ultrahuman_mcp/dist/index.js"]`
- **Environment**: `ULTRAHUMAN_API_KEY=your_key`
- **Transport**: `stdio`
---
## Common Parameters
Most tools accept the following **optional** filter parameters:
| Parameter | Type | Format | Description |
|---|---|---|---|
| `date` | `string` | `YYYY-MM-DD` | Target date. Defaults to **today** if omitted. |
| `last_minutes` | `number` | integer | Restrict data to the last N minutes from now. Example: `30` returns data from the last 30 minutes. |
| `start_time` | `string` | `HH:MM` (24h) | Start of a time window on the given date. Example: `"09:00"` |
| `end_time` | `string` | `HH:MM` (24h) | End of a time window on the given date. Example: `"17:00"` |
| `format` | `string` | `"summary"` \| `"detailed"` | `"summary"` (default) returns aggregated statistics. `"detailed"` returns raw time-series arrays. |
> **Note:** `last_minutes` takes precedence over `start_time`/`end_time`. If neither is specified, the full day's data is used. The `format` parameter is only supported by `get_heart_vitals`.
### Time Filtering Behavior
- **`last_minutes=30`** β Computes an epoch range from `(now - 30min)` to `now` and filters all time-series readings.
- **`start_time="09:00"` + `end_time="17:00"`** β Filters readings to the 9 AM β 5 PM window on the specified date.
- **No time filter** β Returns the complete day's data.
- **Empty result in time range** β If no readings fall in the requested window, the summary will include a `message` field explaining when the latest available reading was, along with `latest_available` (ISO timestamp) and `minutes_since_last_reading`.
---
## Tools Reference
### 1. `get_health_summary`
> **Best starting tool.** Use this first to get a high-level overview of the user's health before drilling into specifics.
Returns a comprehensive health overview combining sleep, recovery, activity, vitals, and AI-generated insights.
#### Input Schema
```json
{
"date": "2025-01-27", // optional, defaults to today
"last_minutes": 60, // optional
"start_time": "06:00", // optional
"end_time": "22:00" // optional
}
```
All parameters are **optional**. Calling with `{}` returns today's full summary.
#### Response Schema
```json
{
"date": "2025-01-27",
"time_range": { // null if no time filter applied
"start": "2025-01-27T06:00:00.000Z",
"end": "2025-01-27T22:00:00.000Z"
},
"scores": {
"sleep": 82, // 0β100, null if unavailable
"recovery": 75, // 0β100, null if unavailable
"activity": 6.5 // movement index, null if unavailable
},
"vitals": {
"resting_hr": 58, // BPM, null if unavailable
"avg_hrv": 45, // ms, null if unavailable
"avg_spo2": 97.2, // %, null if unavailable
"body_temp": 36.5 // Β°C, null if unavailable
},
"activity": {
"total_steps": 8432,
"active_minutes": 45, // null if unavailable
"vo2_max": 42.3 // ml/kg/min, null if unavailable
},
"sleep": {
"total_hours": 7.2, // null if unavailable
"efficiency": 88, // %, null if unavailable
"quality": "good" // "excellent" | "good" | "fair" | "poor"
},
"insights": [
"Excellent sleep quality last night",
"Body is well recovered and ready for activity",
"Great job! Step goal achieved"
]
}
```
#### Insight Generation Rules
| Condition | Insight |
|---|---|
| Sleep score β₯ 80 | "Excellent sleep quality last night" |
| Sleep score < 60 | "Sleep quality could be improved" |
| Recovery score β₯ 80 | "Body is well recovered and ready for activity" |
| Recovery score < 50 | "Consider lighter activity today for recovery" |
| Steps β₯ 10,000 | "Great job! Step goal achieved" |
| Steps < 3,000 | "Consider increasing movement today" |
| Heart rate trend = rising | "Heart rate trending upward" |
---
### 2. `get_heart_vitals`
Returns heart rate, HRV (heart rate variability), and SpO2 (blood oxygen) metrics. Supports both **summary** and **detailed** output formats.
#### Input Schema
```json
{
"date": "2025-01-27", // optional
"last_minutes": 30, // optional
"start_time": "09:00", // optional
"end_time": "17:00", // optional
"format": "summary" // optional: "summary" (default) | "detailed"
}
```
#### Response Schema β Summary (default)
```json
{
"heart_rate": {
"count": 482,
"avg": 72.3,
"min": 54,
"max": 132,
"latest": 68,
"trend": "stable", // "rising" | "falling" | "stable"
"time_range": {
"start": "2025-01-27T00:05:00.000Z",
"end": "2025-01-27T23:55:00.000Z"
},
"resting_hr": 56 // from night/sleep RHR, null if unavailable
},
"hrv": {
"count": 240,
"avg": 42.8,
"min": 18,
"max": 89,
"latest": 45,
"trend": "rising",
"time_range": { "start": "...", "end": "..." },
"avg_sleep_hrv": 52 // null if unavailable
},
"spo2": {
"count": 120,
"avg": 97.1,
"min": 94,
"max": 99,
"latest": 97,
"trend": "stable",
"time_range": { "start": "...", "end": "..." }
}
}
```
#### Response Schema β Detailed (`format: "detailed"`)
```json
{
"hr": [
{ "value": 72, "timestamp": 1706313600 },
{ "value": 68, "timestamp": 1706313900 }
],
"hrv": [
{ "value": 45, "timestamp": 1706313600 }
],
"spo2": [
{ "value": 97, "timestamp": 1706313600 }
],
"night_rhr": 56,
"sleep_rhr": 54,
"avg_sleep_hrv": 52
}
```
#### Trend Calculation
Trend is determined by comparing the **average of the first third** of readings to the **average of the last third**:
- **Rising**: Last third avg is >5% higher than first third avg
- **Falling**: Last third avg is >5% lower than first third avg
- **Stable**: Within Β±5%
---
### 3. `get_sleep_analysis`
Returns comprehensive sleep metrics for a given night. Sleep data is a **nightly aggregate** β time filters (`last_minutes`, `start_time`, `end_time`) are not applicable.
#### Input Schema
```json
{
"date": "2025-01-27" // optional, defaults to today
}
```
#### Response Schema
```json
{
"sleep_score": 82, // 0β100, null if no data
"total_sleep_hours": 7.2, // decimal hours, null if no data
"sleep_efficiency_percent": 88, // %, null if no data
"time_in_bed_hours": 8.1, // decimal hours, null if no data
"rem_sleep_hours": 1.8, // decimal hours, null if no data
"deep_sleep_hours": 1.5, // decimal hours, null if no data
"light_sleep_hours": 3.9, // decimal hours, null if no data
"restorative_sleep_percent": 42, // %, null if no data
"full_sleep_cycles": 4, // integer count, null if no data
"tosses_and_turns": 12, // integer count, null if no data
"morning_alertness_minutes": 15, // minutes, null if no data
"quality": "good" // "excellent" | "good" | "fair" | "poor"
}
```
#### Quality Classification
| Sleep Score | Quality |
|---|---|
| β₯ 85 | `"excellent"` |
| 70 β 84 | `"good"` |
| 50 β 69 | `"fair"` |
| < 50 | `"poor"` |
---
### 4. `get_activity_data`
Returns activity and movement metrics including step counts, active minutes, and fitness indicators.
#### Input Schema
```json
{
"date": "2025-01-27", // optional
"last_minutes": 60, // optional
"start_time": "06:00", // optional
"end_time": "22:00" // optional
}
```
#### Response Schema
```json
{
"total_steps": 8432,
"steps_trend": { // TimeSeriesSummary of step readings
"count": 96,
"avg": 87.8,
"min": 0,
"max": 520,
"latest": 42,
"trend": "falling",
"time_range": { "start": "...", "end": "..." }
},
"active_minutes": 45, // null if unavailable
"movement_index": 6.5, // null if unavailable
"vo2_max": 42.3, // ml/kg/min, null if unavailable
"activity_level": "active" // classification (see table below)
}
```
#### Activity Level Classification
| Total Steps | Level |
|---|---|
| β₯ 12,000 | `"very_active"` |
| 8,000 β 11,999 | `"active"` |
| 5,000 β 7,999 | `"moderate"` |
| < 5,000 | `"sedentary"` |
> **Note:** When a time filter is applied, `total_steps` represents the sum of steps within that window only. Activity level classification still applies to the filtered total.
---
### 5. `get_recovery_score`
Returns recovery status indicating the body's readiness for physical activity. Recovery data is a **daily aggregate** β time filters are not applicable.
#### Input Schema
```json
{
"date": "2025-01-27" // optional, defaults to today
}
```
#### Response Schema
```json
{
"recovery_score": 75, // 0β100, null if no data
"recovery_index": 75, // same as recovery_score
"status": "good" // "excellent" | "good" | "fair" | "poor"
}
```
#### Recovery Status Classification
| Recovery Score | Status |
|---|---|
| β₯ 80 | `"excellent"` |
| 60 β 79 | `"good"` |
| 40 β 59 | `"fair"` |
| < 40 | `"poor"` |
---
### 6. `get_temperature_data`
Returns body temperature metrics including skin temperature readings, baseline deviation, and average body temperature. Useful for detecting illness, stress, or menstrual cycle patterns.
#### Input Schema
```json
{
"date": "2025-01-27", // optional
"last_minutes": 60, // optional
"start_time": "22:00", // optional
"end_time": "06:00" // optional
}
```
#### Response Schema
```json
{
"readings": { // TimeSeriesSummary of temperature readings
"count": 240,
"avg": 33.2, // Β°C (skin temperature)
"min": 31.8,
"max": 34.5,
"latest": 33.0,
"trend": "stable",
"time_range": { "start": "...", "end": "..." }
},
"temperature_deviation": 0.3, // Β°C deviation from baseline, null if unavailable
"average_body_temperature": 36.5 // Β°C, null if unavailable
}
```
---
### 7. `get_daily_metrics`
> **Raw data tool.** Returns the complete, unprocessed API response from the Ultrahuman Partner API. Use this only when you need access to metric types not covered by the other tools, or when you need the original API structure.
#### Input Schema
```json
{
"date": "2025-01-27", // optional, defaults to today
"start_date": "2025-01-20", // optional, for date range queries
"end_date": "2025-01-27" // optional, for date range queries
}
```
> **Note:** Use either `date` (single day) OR `start_date` + `end_date` (range). If none are provided, defaults to today.
#### Response Schema
Returns the raw Ultrahuman API response:
```json
{
"status": "success",
"error": null,
"data": {
"metrics": {
"2025-01-27": [
{
"type": "hr",
"object": {
"values": [
{ "value": 72, "timestamp": 1706313600 }
]
}
},
{
"type": "hrv",
"object": { "..." }
},
{
"type": "sleep",
"object": { "..." }
}
]
}
}
}
```
Known metric types in the raw response: `hr`, `hrv`, `spo2`, `night_rhr`, `sleep_rhr`, `avg_sleep_hrv`, `sleep`, `steps`, `movement_index`, `active_minutes`, `vo2_max`, `recovery_index`, `temp`.
---
## Tool Selection Guide
Use this decision tree to pick the right tool:
| User Intent | Recommended Tool |
|---|---|
| "How am I doing today?" / general health check | `get_health_summary` |
| "What's my heart rate?" / "How's my HRV?" / "Check my SpO2" | `get_heart_vitals` |
| "How did I sleep?" / "Sleep quality?" | `get_sleep_analysis` |
| "How many steps?" / "Am I active enough?" | `get_activity_data` |
| "Am I recovered?" / "Ready for a workout?" | `get_recovery_score` |
| "What's my body temperature?" / "Am I running a fever?" | `get_temperature_data` |
| Need raw/unprocessed data or unsupported metric types | `get_daily_metrics` |
### Recommended Agent Workflow
1. **Start with `get_health_summary`** to get the big picture.
2. **Drill into specific tools** based on what the user asks about or what the insights suggest.
3. **Use time filters** (`last_minutes`, `start_time`/`end_time`) to narrow down to relevant windows.
4. **Use `format: "detailed"`** on `get_heart_vitals` only when the user wants to see individual readings or plot data.
5. **Fall back to `get_daily_metrics`** only when none of the specialized tools cover the requested data.
---
## Response Conventions
### TimeSeriesSummary Object
Many tools return a `TimeSeriesSummary` object for time-series metrics. Its shape is:
```json
{
"count": 482, // number of data points
"avg": 72.3, // arithmetic mean
"min": 54, // minimum value
"max": 132, // maximum value
"latest": 68, // most recent reading, null if no data
"trend": "stable", // "rising" | "falling" | "stable"
"time_range": {
"start": "ISO-8601", // timestamp of earliest reading
"end": "ISO-8601" // timestamp of latest reading
},
// Only present when no data in requested time range:
"message": "No data in requested time range. Latest reading was 45 minutes ago.",
"latest_available": "ISO-8601",
"minutes_since_last_reading": 45
}
```
### Null Values
All metric fields may return `null` when:
- The ring wasn't worn during the relevant period
- Data hasn't synced yet
- The metric isn't available for this day
### Error Responses
On failure, tools return:
```json
{
"error": "Description of what went wrong"
}
```
Common errors:
- `"API request failed: 401 Unauthorized"` β Invalid or expired API key
- `"API request failed: 429 Too Many Requests"` β Rate limit hit
- `"Unknown tool: <name>"` β Requested a tool that doesn't exist
---
## Scoring & Classification Thresholds
### Sleep Quality
| Score Range | Classification |
|---|---|
| 85β100 | `excellent` |
| 70β84 | `good` |
| 50β69 | `fair` |
| 0β49 | `poor` |
### Recovery Status
| Score Range | Classification |
|---|---|
| 80β100 | `excellent` |
| 60β79 | `good` |
| 40β59 | `fair` |
| 0β39 | `poor` |
### Activity Level
| Step Count | Classification |
|---|---|
| β₯ 12,000 | `very_active` |
| 8,000β11,999 | `active` |
| 5,000β7,999 | `moderate` |
| < 5,000 | `sedentary` |
### Heart Rate Trend
| Metric | Threshold |
|---|---|
| Rising | Last third avg > first third avg by >5% |
| Falling | Last third avg < first third avg by >5% |
| Stable | Within Β±5% |
---
## Example Agent Conversations
### Quick Health Check
> **User:** "How am I doing today?"
```
Agent calls: get_health_summary({})
β Reviews scores, vitals, and insights
β "Your sleep was good at 82/100 with 7.2 hours. Recovery is at 75 β you're in good shape for moderate activity. You've taken 8,432 steps so far. Your resting heart rate is 58 bpm, which is normal."
```
### Morning Routine Check
> **User:** "How was my sleep and am I ready for a run?"
```
Agent calls: get_sleep_analysis({}) + get_recovery_score({})
β "You slept 7.2 hours with 88% efficiency β quality is 'good'. Your recovery score is 75/100 (good), so you're ready for a run, but maybe not an intense sprint session."
```
### Real-Time Heart Monitoring
> **User:** "What's my heart rate been like in the last hour?"
```
Agent calls: get_heart_vitals({ "last_minutes": 60 })
β "Over the last 60 minutes, your heart rate averaged 78 bpm (range 65β95). Trend is stable. HRV avg is 38ms."
```
### Post-Workout Analysis
> **User:** "How was my heart rate during my workout from 6 to 7 PM?"
```
Agent calls: get_heart_vitals({ "start_time": "18:00", "end_time": "19:00", "format": "detailed" })
β Returns individual HR, HRV, and SpO2 readings with timestamps for the workout window.
```
### Historical Check
> **User:** "Compare my recovery yesterday vs today"
```
Agent calls: get_recovery_score({ "date": "2025-01-26" }) + get_recovery_score({})
β "Yesterday your recovery was 62 (good), today it's 75 (good) β trending upward. You're more recovered today."
```
### Temperature Concern
> **User:** "I feel unwell, is my temperature elevated?"
```
Agent calls: get_temperature_data({})
β "Your skin temperature deviation from baseline is +0.8Β°C, which is higher than normal. Average body temperature is 37.1Β°C. You may want to monitor this."
```
---
## Project Structure
```
ultrahuman_mcp/
βββ src/
β βββ index.ts # MCP server β tool definitions & request routing
β βββ ultrahuman-client.ts # API client β data fetching, filtering, summarization
β βββ types.ts # TypeScript interfaces for API responses
βββ dist/ # Compiled JavaScript (generated by `npm run build`)
βββ package.json # Dependencies & scripts
βββ tsconfig.json # TypeScript configuration
βββ .env # Your API key (gitignored)
βββ .env.example # API key template
βββ .gitignore
βββ README.md
```
---
## License
MIT License
---
## Links
- [Ultrahuman Ring](https://ultrahuman.com/ring)
- [Ultrahuman Vision (API Portal)](https://vision.ultrahuman.com)
- [Model Context Protocol](https://modelcontextprotocol.io)
- [MCP TypeScript SDK](https://github.com/modelcontextprotocol/typescript-sdk)
TDQS
Scored across 7 tools
Each tool targets a specific health metric (recovery, temperature, heart, sleep, activity), but get_health_summary and get_daily_metrics overlap with the specific tools, though descriptions clarify their distinct purposes as summary and raw access.
All tools follow a consistent get_<metric> naming pattern, making it easy to predict tool names and purposes.
Seven tools is well-scoped for a health data server, covering the major health domains without unnecessary redundancy.
The tool set covers key health metrics (recovery, temperature, heart, sleep, activity) and provides both summary and raw data access. Minor gaps include lack of historical trend queries beyond recent minutes, but core read-only health tracking is well covered.