Skip to main content
Glama

Oura Ring MCP Server

by JamesLouie
oura-client.ts10.5 kB
import axios, { AxiosInstance, AxiosError } from 'axios'; import { PersonalInfoResponse, SleepResponse, ActivityResponse, ReadinessResponse, HeartRateResponse, WorkoutResponse, SessionResponse, TagResponse, DailyActivityResponse, DailySleepResponse, DailyReadinessResponse, DailyStressResponse, WebhookSubscriptionResponse, DateRangeParams, WebhookSubscriptionCreateParams, PersonalInfoResponseSchema, SleepResponseSchema, ActivityResponseSchema, ReadinessResponseSchema, HeartRateResponseSchema, WorkoutResponseSchema, SessionResponseSchema, TagResponseSchema, DailyActivityResponseSchema, DailySleepResponseSchema, DailyReadinessResponseSchema, DailyStressResponseSchema, WebhookSubscriptionResponseSchema, } from './types.js'; export interface OuraClientConfig { accessToken: string; baseUrl?: string; clientId?: string; clientSecret?: string; } export class OuraAPIError extends Error { constructor( message: string, public statusCode?: number, public response?: any ) { super(message); this.name = 'OuraAPIError'; } } export class OuraClient { private client: AxiosInstance; private accessToken: string; private clientId?: string; private clientSecret?: string; constructor(config: OuraClientConfig) { this.accessToken = config.accessToken; this.clientId = config.clientId; this.clientSecret = config.clientSecret; this.client = axios.create({ baseURL: config.baseUrl || 'https://api.ouraring.com', timeout: 30000, headers: { 'Authorization': `Bearer ${this.accessToken}`, 'Content-Type': 'application/json', }, }); // Add response interceptor for error handling this.client.interceptors.response.use( (response) => response, (error: AxiosError) => { if (error.response) { throw new OuraAPIError( `API Error: ${error.response.statusText}`, error.response.status, error.response.data ); } else if (error.request) { throw new OuraAPIError('Network Error: No response received'); } else { throw new OuraAPIError(`Request Error: ${error.message}`); } } ); } private formatParams(params: DateRangeParams): URLSearchParams { const searchParams = new URLSearchParams(); if (params.start_date) searchParams.append('start_date', params.start_date); if (params.end_date) searchParams.append('end_date', params.end_date); if (params.next_token) searchParams.append('next_token', params.next_token); return searchParams; } // Personal Info async getPersonalInfo(): Promise<PersonalInfoResponse> { const response = await this.client.get('/v2/usercollection/personal_info'); const parsed = PersonalInfoResponseSchema.parse(response.data); // Handle both wrapped and unwrapped response formats if ('data' in parsed) { return parsed; } else { return { data: parsed }; } } // Sleep data async getSleep(params: DateRangeParams = {}): Promise<SleepResponse> { const searchParams = this.formatParams(params); const url = `/v2/usercollection/sleep${searchParams.toString() ? `?${searchParams.toString()}` : ''}`; const response = await this.client.get(url); return SleepResponseSchema.parse(response.data); } // Activity data async getActivity(params: DateRangeParams = {}): Promise<ActivityResponse> { const searchParams = this.formatParams(params); const url = `/v2/usercollection/activity${searchParams.toString() ? `?${searchParams.toString()}` : ''}`; const response = await this.client.get(url); return ActivityResponseSchema.parse(response.data); } // Readiness data async getReadiness(params: DateRangeParams = {}): Promise<ReadinessResponse> { const searchParams = this.formatParams(params); const url = `/v2/usercollection/readiness${searchParams.toString() ? `?${searchParams.toString()}` : ''}`; const response = await this.client.get(url); return ReadinessResponseSchema.parse(response.data); } // Heart Rate data async getHeartRate(params: DateRangeParams = {}): Promise<HeartRateResponse> { const searchParams = this.formatParams(params); const url = `/v2/usercollection/heartrate${searchParams.toString() ? `?${searchParams.toString()}` : ''}`; const response = await this.client.get(url); return HeartRateResponseSchema.parse(response.data); } // Workout data async getWorkouts(params: DateRangeParams = {}): Promise<WorkoutResponse> { const searchParams = this.formatParams(params); const url = `/v2/usercollection/workout${searchParams.toString() ? `?${searchParams.toString()}` : ''}`; const response = await this.client.get(url); return WorkoutResponseSchema.parse(response.data); } // Session data async getSessions(params: DateRangeParams = {}): Promise<SessionResponse> { const searchParams = this.formatParams(params); const url = `/v2/usercollection/session${searchParams.toString() ? `?${searchParams.toString()}` : ''}`; const response = await this.client.get(url); return SessionResponseSchema.parse(response.data); } // Tag data async getTags(params: DateRangeParams = {}): Promise<TagResponse> { const searchParams = this.formatParams(params); const url = `/v2/usercollection/tag${searchParams.toString() ? `?${searchParams.toString()}` : ''}`; const response = await this.client.get(url); return TagResponseSchema.parse(response.data); } // Enhanced Tag data async getEnhancedTags(params: DateRangeParams = {}): Promise<TagResponse> { const searchParams = this.formatParams(params); const url = `/v2/usercollection/enhanced_tag${searchParams.toString() ? `?${searchParams.toString()}` : ''}`; const response = await this.client.get(url); return TagResponseSchema.parse(response.data); } // Daily Activity data async getDailyActivity(params: DateRangeParams = {}): Promise<DailyActivityResponse> { const searchParams = this.formatParams(params); const url = `/v2/usercollection/daily_activity${searchParams.toString() ? `?${searchParams.toString()}` : ''}`; const response = await this.client.get(url); return DailyActivityResponseSchema.parse(response.data); } // Daily Sleep data async getDailySleep(params: DateRangeParams = {}): Promise<DailySleepResponse> { const searchParams = this.formatParams(params); const url = `/v2/usercollection/daily_sleep${searchParams.toString() ? `?${searchParams.toString()}` : ''}`; const response = await this.client.get(url); return DailySleepResponseSchema.parse(response.data); } // Daily Readiness data async getDailyReadiness(params: DateRangeParams = {}): Promise<DailyReadinessResponse> { const searchParams = this.formatParams(params); const url = `/v2/usercollection/daily_readiness${searchParams.toString() ? `?${searchParams.toString()}` : ''}`; const response = await this.client.get(url); return DailyReadinessResponseSchema.parse(response.data); } // Daily Stress data async getDailyStress(params: DateRangeParams = {}): Promise<DailyStressResponse> { const searchParams = this.formatParams(params); const url = `/v2/usercollection/daily_stress${searchParams.toString() ? `?${searchParams.toString()}` : ''}`; const response = await this.client.get(url); return DailyStressResponseSchema.parse(response.data); } // Webhook Subscriptions async getWebhookSubscriptions(): Promise<WebhookSubscriptionResponse> { if (!this.clientId || !this.clientSecret) { throw new OuraAPIError('Client ID and Client Secret are required for webhook operations'); } const response = await this.client.get('/v2/webhook/subscription', { headers: { 'x-client-id': this.clientId, 'x-client-secret': this.clientSecret, }, }); return WebhookSubscriptionResponseSchema.parse(response.data); } async createWebhookSubscription(params: WebhookSubscriptionCreateParams): Promise<any> { if (!this.clientId || !this.clientSecret) { throw new OuraAPIError('Client ID and Client Secret are required for webhook operations'); } const response = await this.client.post('/v2/webhook/subscription', params, { headers: { 'x-client-id': this.clientId, 'x-client-secret': this.clientSecret, }, }); return response.data; } async updateWebhookSubscription(id: string, params: Partial<WebhookSubscriptionCreateParams>): Promise<any> { if (!this.clientId || !this.clientSecret) { throw new OuraAPIError('Client ID and Client Secret are required for webhook operations'); } const response = await this.client.put(`/v2/webhook/subscription/${id}`, params, { headers: { 'x-client-id': this.clientId, 'x-client-secret': this.clientSecret, }, }); return response.data; } async deleteWebhookSubscription(id: string): Promise<void> { if (!this.clientId || !this.clientSecret) { throw new OuraAPIError('Client ID and Client Secret are required for webhook operations'); } await this.client.delete(`/v2/webhook/subscription/${id}`, { headers: { 'x-client-id': this.clientId, 'x-client-secret': this.clientSecret, }, }); } // Sleep Time data async getSleepTime(params: DateRangeParams = {}): Promise<any> { const searchParams = this.formatParams(params); const url = `/v2/usercollection/sleep_time${searchParams.toString() ? `?${searchParams.toString()}` : ''}`; const response = await this.client.get(url); return response.data; } // Rest Mode Periods async getRestModePeriods(params: DateRangeParams = {}): Promise<any> { const searchParams = this.formatParams(params); const url = `/v2/usercollection/rest_mode_period${searchParams.toString() ? `?${searchParams.toString()}` : ''}`; const response = await this.client.get(url); return response.data; } // Ring Configuration async getRingConfiguration(params: DateRangeParams = {}): Promise<any> { const searchParams = this.formatParams(params); const url = `/v2/usercollection/ring_configuration${searchParams.toString() ? `?${searchParams.toString()}` : ''}`; const response = await this.client.get(url); return response.data; } }

Latest Blog Posts

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/JamesLouie/oura-ring-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server