Skip to main content
Glama

Oura Ring MCP Server

by JamesLouie
server.ts20.1 kB
import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { CallToolRequestSchema, ListToolsRequestSchema, Tool, } from '@modelcontextprotocol/sdk/types.js'; import { OuraClient, OuraClientConfig, OuraAPIError } from './oura-client.js'; import { DateRangeParams, WebhookSubscriptionCreateParams } from './types.js'; interface MCPServerConfig { oura: OuraClientConfig; } export class OuraMCPServer { private server: Server; private ouraClient: OuraClient; constructor(config: MCPServerConfig) { this.server = new Server( { name: 'oura-ring-mcp', version: '0.1.0', } ); this.ouraClient = new OuraClient(config.oura); this.setupHandlers(); } private setupHandlers(): void { // List available tools this.server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: this.getAvailableTools(), }; }); // Handle tool calls this.server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; try { return await this.handleToolCall(name, args); } catch (error) { if (error instanceof OuraAPIError) { throw new Error(`Oura API Error: ${error.message}`); } throw error; } }); } private getAvailableTools(): Tool[] { return [ // Personal Info { name: 'get_personal_info', description: 'Get user personal information including age, weight, height, biological sex, and timezone', inputSchema: { type: 'object', properties: {}, }, }, // Sleep Data { name: 'get_sleep', description: 'Get sleep data including sleep scores, contributors, and sleep stages', inputSchema: { type: 'object', properties: { start_date: { type: 'string', description: 'Start date in YYYY-MM-DD format', pattern: '^\\d{4}-\\d{2}-\\d{2}$', }, end_date: { type: 'string', description: 'End date in YYYY-MM-DD format', pattern: '^\\d{4}-\\d{2}-\\d{2}$', }, next_token: { type: 'string', description: 'Token for pagination', }, }, }, }, // Activity Data { name: 'get_activity', description: 'Get activity data including steps, calories, and activity levels', inputSchema: { type: 'object', properties: { start_date: { type: 'string', description: 'Start date in YYYY-MM-DD format', pattern: '^\\d{4}-\\d{2}-\\d{2}$', }, end_date: { type: 'string', description: 'End date in YYYY-MM-DD format', pattern: '^\\d{4}-\\d{2}-\\d{2}$', }, next_token: { type: 'string', description: 'Token for pagination', }, }, }, }, // Readiness Data { name: 'get_readiness', description: 'Get readiness data including readiness score and contributing factors', inputSchema: { type: 'object', properties: { start_date: { type: 'string', description: 'Start date in YYYY-MM-DD format', pattern: '^\\d{4}-\\d{2}-\\d{2}$', }, end_date: { type: 'string', description: 'End date in YYYY-MM-DD format', pattern: '^\\d{4}-\\d{2}-\\d{2}$', }, next_token: { type: 'string', description: 'Token for pagination', }, }, }, }, // Heart Rate Data { name: 'get_heart_rate', description: 'Get heart rate data with timestamps and sources', inputSchema: { type: 'object', properties: { start_date: { type: 'string', description: 'Start date in YYYY-MM-DD format', pattern: '^\\d{4}-\\d{2}-\\d{2}$', }, end_date: { type: 'string', description: 'End date in YYYY-MM-DD format', pattern: '^\\d{4}-\\d{2}-\\d{2}$', }, next_token: { type: 'string', description: 'Token for pagination', }, }, }, }, // Workout Data { name: 'get_workouts', description: 'Get workout data including activity type, duration, calories, and distance', inputSchema: { type: 'object', properties: { start_date: { type: 'string', description: 'Start date in YYYY-MM-DD format', pattern: '^\\d{4}-\\d{2}-\\d{2}$', }, end_date: { type: 'string', description: 'End date in YYYY-MM-DD format', pattern: '^\\d{4}-\\d{2}-\\d{2}$', }, next_token: { type: 'string', description: 'Token for pagination', }, }, }, }, // Session Data { name: 'get_sessions', description: 'Get session data for breathing, meditation, naps, and other activities', inputSchema: { type: 'object', properties: { start_date: { type: 'string', description: 'Start date in YYYY-MM-DD format', pattern: '^\\d{4}-\\d{2}-\\d{2}$', }, end_date: { type: 'string', description: 'End date in YYYY-MM-DD format', pattern: '^\\d{4}-\\d{2}-\\d{2}$', }, next_token: { type: 'string', description: 'Token for pagination', }, }, }, }, // Tag Data { name: 'get_tags', description: 'Get user-created tags with timestamps', inputSchema: { type: 'object', properties: { start_date: { type: 'string', description: 'Start date in YYYY-MM-DD format', pattern: '^\\d{4}-\\d{2}-\\d{2}$', }, end_date: { type: 'string', description: 'End date in YYYY-MM-DD format', pattern: '^\\d{4}-\\d{2}-\\d{2}$', }, next_token: { type: 'string', description: 'Token for pagination', }, }, }, }, // Enhanced Tag Data { name: 'get_enhanced_tags', description: 'Get enhanced tags with additional metadata', inputSchema: { type: 'object', properties: { start_date: { type: 'string', description: 'Start date in YYYY-MM-DD format', pattern: '^\\d{4}-\\d{2}-\\d{2}$', }, end_date: { type: 'string', description: 'End date in YYYY-MM-DD format', pattern: '^\\d{4}-\\d{2}-\\d{2}$', }, next_token: { type: 'string', description: 'Token for pagination', }, }, }, }, // Daily Activity Data { name: 'get_daily_activity', description: 'Get daily activity summaries with scores and metrics', inputSchema: { type: 'object', properties: { start_date: { type: 'string', description: 'Start date in YYYY-MM-DD format', pattern: '^\\d{4}-\\d{2}-\\d{2}$', }, end_date: { type: 'string', description: 'End date in YYYY-MM-DD format', pattern: '^\\d{4}-\\d{2}-\\d{2}$', }, next_token: { type: 'string', description: 'Token for pagination', }, }, }, }, // Daily Sleep Data { name: 'get_daily_sleep', description: 'Get daily sleep summaries with scores and contributors', inputSchema: { type: 'object', properties: { start_date: { type: 'string', description: 'Start date in YYYY-MM-DD format', pattern: '^\\d{4}-\\d{2}-\\d{2}$', }, end_date: { type: 'string', description: 'End date in YYYY-MM-DD format', pattern: '^\\d{4}-\\d{2}-\\d{2}$', }, next_token: { type: 'string', description: 'Token for pagination', }, }, }, }, // Daily Readiness Data { name: 'get_daily_readiness', description: 'Get daily readiness summaries with scores and temperature data', inputSchema: { type: 'object', properties: { start_date: { type: 'string', description: 'Start date in YYYY-MM-DD format', pattern: '^\\d{4}-\\d{2}-\\d{2}$', }, end_date: { type: 'string', description: 'End date in YYYY-MM-DD format', pattern: '^\\d{4}-\\d{2}-\\d{2}$', }, next_token: { type: 'string', description: 'Token for pagination', }, }, }, }, // Daily Stress Data { name: 'get_daily_stress', description: 'Get daily stress levels and recovery data', inputSchema: { type: 'object', properties: { start_date: { type: 'string', description: 'Start date in YYYY-MM-DD format', pattern: '^\\d{4}-\\d{2}-\\d{2}$', }, end_date: { type: 'string', description: 'End date in YYYY-MM-DD format', pattern: '^\\d{4}-\\d{2}-\\d{2}$', }, next_token: { type: 'string', description: 'Token for pagination', }, }, }, }, // Sleep Time Data { name: 'get_sleep_time', description: 'Get sleep time data including bedtime and wake time', inputSchema: { type: 'object', properties: { start_date: { type: 'string', description: 'Start date in YYYY-MM-DD format', pattern: '^\\d{4}-\\d{2}-\\d{2}$', }, end_date: { type: 'string', description: 'End date in YYYY-MM-DD format', pattern: '^\\d{4}-\\d{2}-\\d{2}$', }, next_token: { type: 'string', description: 'Token for pagination', }, }, }, }, // Rest Mode Periods { name: 'get_rest_mode_periods', description: 'Get rest mode period data when user has enabled rest mode', inputSchema: { type: 'object', properties: { start_date: { type: 'string', description: 'Start date in YYYY-MM-DD format', pattern: '^\\d{4}-\\d{2}-\\d{2}$', }, end_date: { type: 'string', description: 'End date in YYYY-MM-DD format', pattern: '^\\d{4}-\\d{2}-\\d{2}$', }, next_token: { type: 'string', description: 'Token for pagination', }, }, }, }, // Ring Configuration { name: 'get_ring_configuration', description: 'Get ring configuration settings and preferences', inputSchema: { type: 'object', properties: { start_date: { type: 'string', description: 'Start date in YYYY-MM-DD format', pattern: '^\\d{4}-\\d{2}-\\d{2}$', }, end_date: { type: 'string', description: 'End date in YYYY-MM-DD format', pattern: '^\\d{4}-\\d{2}-\\d{2}$', }, next_token: { type: 'string', description: 'Token for pagination', }, }, }, }, // Webhook Subscriptions { name: 'get_webhook_subscriptions', description: 'Get list of active webhook subscriptions', inputSchema: { type: 'object', properties: {}, }, }, { name: 'create_webhook_subscription', description: 'Create a new webhook subscription for real-time data updates', inputSchema: { type: 'object', properties: { callback_url: { type: 'string', description: 'The URL where webhook events will be sent', format: 'uri', }, verification_token: { type: 'string', description: 'Secret token used to verify webhook events', }, event_type: { type: 'string', enum: ['create', 'update', 'delete'], description: 'Type of events to subscribe to', }, data_type: { type: 'string', enum: [ 'tag', 'enhanced_tag', 'workout', 'session', 'sleep', 'daily_sleep', 'daily_readiness', 'daily_activity', 'daily_stress', ], description: 'Type of data to receive webhooks for', }, }, required: ['callback_url', 'verification_token', 'event_type', 'data_type'], }, }, { name: 'delete_webhook_subscription', description: 'Delete an existing webhook subscription', inputSchema: { type: 'object', properties: { id: { type: 'string', description: 'ID of the webhook subscription to delete', }, }, required: ['id'], }, }, ]; } private async handleToolCall(name: string, args: any): Promise<any> { const dateRangeParams: DateRangeParams = { start_date: args?.start_date, end_date: args?.end_date, next_token: args?.next_token, }; switch (name) { case 'get_personal_info': return { content: [ { type: 'text', text: JSON.stringify(await this.ouraClient.getPersonalInfo(), null, 2), }, ], }; case 'get_sleep': return { content: [ { type: 'text', text: JSON.stringify(await this.ouraClient.getSleep(dateRangeParams), null, 2), }, ], }; case 'get_activity': return { content: [ { type: 'text', text: JSON.stringify(await this.ouraClient.getActivity(dateRangeParams), null, 2), }, ], }; case 'get_readiness': return { content: [ { type: 'text', text: JSON.stringify(await this.ouraClient.getReadiness(dateRangeParams), null, 2), }, ], }; case 'get_heart_rate': return { content: [ { type: 'text', text: JSON.stringify(await this.ouraClient.getHeartRate(dateRangeParams), null, 2), }, ], }; case 'get_workouts': return { content: [ { type: 'text', text: JSON.stringify(await this.ouraClient.getWorkouts(dateRangeParams), null, 2), }, ], }; case 'get_sessions': return { content: [ { type: 'text', text: JSON.stringify(await this.ouraClient.getSessions(dateRangeParams), null, 2), }, ], }; case 'get_tags': return { content: [ { type: 'text', text: JSON.stringify(await this.ouraClient.getTags(dateRangeParams), null, 2), }, ], }; case 'get_enhanced_tags': return { content: [ { type: 'text', text: JSON.stringify(await this.ouraClient.getEnhancedTags(dateRangeParams), null, 2), }, ], }; case 'get_daily_activity': return { content: [ { type: 'text', text: JSON.stringify(await this.ouraClient.getDailyActivity(dateRangeParams), null, 2), }, ], }; case 'get_daily_sleep': return { content: [ { type: 'text', text: JSON.stringify(await this.ouraClient.getDailySleep(dateRangeParams), null, 2), }, ], }; case 'get_daily_readiness': return { content: [ { type: 'text', text: JSON.stringify(await this.ouraClient.getDailyReadiness(dateRangeParams), null, 2), }, ], }; case 'get_daily_stress': return { content: [ { type: 'text', text: JSON.stringify(await this.ouraClient.getDailyStress(dateRangeParams), null, 2), }, ], }; case 'get_sleep_time': return { content: [ { type: 'text', text: JSON.stringify(await this.ouraClient.getSleepTime(dateRangeParams), null, 2), }, ], }; case 'get_rest_mode_periods': return { content: [ { type: 'text', text: JSON.stringify(await this.ouraClient.getRestModePeriods(dateRangeParams), null, 2), }, ], }; case 'get_ring_configuration': return { content: [ { type: 'text', text: JSON.stringify(await this.ouraClient.getRingConfiguration(dateRangeParams), null, 2), }, ], }; case 'get_webhook_subscriptions': return { content: [ { type: 'text', text: JSON.stringify(await this.ouraClient.getWebhookSubscriptions(), null, 2), }, ], }; case 'create_webhook_subscription': const createParams: WebhookSubscriptionCreateParams = { callback_url: args.callback_url, verification_token: args.verification_token, event_type: args.event_type, data_type: args.data_type, }; return { content: [ { type: 'text', text: JSON.stringify(await this.ouraClient.createWebhookSubscription(createParams), null, 2), }, ], }; case 'delete_webhook_subscription': await this.ouraClient.deleteWebhookSubscription(args.id); return { content: [ { type: 'text', text: `Webhook subscription ${args.id} deleted successfully`, }, ], }; default: throw new Error(`Unknown tool: ${name}`); } } public getServer(): Server { return this.server; } }

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