Skip to main content
Glama
heart-rate.ts6.05 kB
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import { z } from 'zod'; import { makeFitbitRequest, ToolResponseStructure } from './utils.js'; const FITBIT_API_BASE = 'https://api.fitbit.com/1'; // --- Fitbit API Response Interfaces for Heart Rate --- // Represents a heart rate zone from the Fitbit API interface HeartRateZone { caloriesOut: number; max: number; min: number; minutes: number; name: string; } // Represents heart rate data for a single day interface HeartRateValue { heartRateZones: HeartRateZone[]; restingHeartRate?: number; } // Represents a single heart rate entry from the Fitbit Time Series API interface HeartRateTimeSeriesEntry { dateTime: string; value: HeartRateValue; } // Represents the structure of the response from the Fitbit Time Series API for heart rate interface HeartRateTimeSeriesResponse { 'activities-heart': HeartRateTimeSeriesEntry[]; } // --- Tool Registration --- /** * Registers Fitbit heart rate tools with the MCP server. * @param server The McpServer instance. * @param getAccessTokenFn Function to retrieve the current access token. */ export function registerHeartRateTools( server: McpServer, getAccessTokenFn: () => Promise<string | null> ): void { // --- Heart Rate by Date/Period Tool --- const periodToolName = 'get_heart_rate'; const periodDescription = "Get the raw JSON response for heart rate data from Fitbit for a specified period ending today or on a specific date. Requires a 'period' parameter such as '1d', '7d', '30d', '1w', '1m' and optionally accepts 'date' parameter."; const periodParametersSchemaShape = { period: z .enum(['1d', '7d', '30d', '1w', '1m']) .describe('The time period for which to retrieve heart rate data.'), date: z .string() .regex( /^\d{4}-\d{2}-\d{2}$|^today$/, "Date must be in YYYY-MM-DD format or 'today'." ) .optional() .describe( "The date for which to retrieve heart rate data (YYYY-MM-DD or 'today'). Defaults to 'today'." ), }; type HeartRatePeriodParams = { period: '1d' | '7d' | '30d' | '1w' | '1m'; date?: string; }; server.tool( periodToolName, periodDescription, periodParametersSchemaShape, async ({ period, date = 'today', }: HeartRatePeriodParams): Promise<ToolResponseStructure> => { // Construct the endpoint dynamically const endpoint = `activities/heart/date/${date}/${period}.json`; const heartRateData = await makeFitbitRequest<HeartRateTimeSeriesResponse>( endpoint, getAccessTokenFn, FITBIT_API_BASE ); // Handle API call failure if (!heartRateData) { return { content: [ { type: 'text', text: `Failed to retrieve heart rate data from Fitbit API for date '${date}' and period '${period}'. Check token and permissions.`, }, ], isError: true, }; } // Handle no data found for the period const heartRateEntries = heartRateData['activities-heart'] || []; if (heartRateEntries.length === 0) { return { content: [ { type: 'text', text: `No heart rate data found for date '${date}' and period '${period}'.`, }, ], }; } // Return successful response with raw JSON const rawJsonResponse = JSON.stringify(heartRateData, null, 2); return { content: [{ type: 'text', text: rawJsonResponse }], }; } ); // --- Heart Rate by Date Range Tool --- const rangeToolName = 'get_heart_rate_by_date_range'; const rangeDescription = "Get the raw JSON response for heart rate data from Fitbit for a specific date range. Requires 'startDate' and 'endDate' parameters in 'YYYY-MM-DD' format. Note: The API enforces a maximum range of 1 year."; const rangeParametersSchemaShape = { startDate: z .string() .regex(/^\d{4}-\d{2}-\d{2}$/, 'Start date must be in YYYY-MM-DD format.') .describe( 'The start date for which to retrieve heart rate data (YYYY-MM-DD).' ), endDate: z .string() .regex(/^\d{4}-\d{2}-\d{2}$/, 'End date must be in YYYY-MM-DD format.') .describe( 'The end date for which to retrieve heart rate data (YYYY-MM-DD).' ), }; type HeartRateRangeParams = { startDate: string; endDate: string; }; server.tool( rangeToolName, rangeDescription, rangeParametersSchemaShape, async ({ startDate, endDate, }: HeartRateRangeParams): Promise<ToolResponseStructure> => { // Construct the endpoint dynamically const endpoint = `activities/heart/date/${startDate}/${endDate}.json`; // Make the request const heartRateData = await makeFitbitRequest<HeartRateTimeSeriesResponse>( endpoint, getAccessTokenFn, FITBIT_API_BASE ); // Handle API call failure if (!heartRateData) { return { content: [ { type: 'text', text: `Failed to retrieve heart rate data from Fitbit API for the date range '${startDate}' to '${endDate}'. Check token, permissions, date format, and ensure the range is 1 year or less.`, }, ], isError: true, }; } // Handle no data found const heartRateEntries = heartRateData['activities-heart'] || []; if (heartRateEntries.length === 0) { return { content: [ { type: 'text', text: `No heart rate data found for the date range '${startDate}' to '${endDate}'.`, }, ], }; } // Return successful response const rawJsonResponse = JSON.stringify(heartRateData, null, 2); return { content: [{ type: 'text', text: rawJsonResponse }], }; } ); }

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/TheDigitalNinja/mcp-fitbit'

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