Skip to main content
Glama
B3r3z

Intervals.icu MCP Server

by B3r3z

Server Configuration

Describes the environment variables required to run the server.

NameRequiredDescriptionDefault
API_KEYYesYour Intervals.icu API key. Generate one via Settings > API.
LOG_LEVELNoLogging level for the server.INFO
ATHLETE_IDYesYour Intervals.icu athlete ID, e.g., 'i12345' as found in your profile URL.
FASTMCP_HOSTNoHost for the SSE MCP server.127.0.0.1
FASTMCP_PORTNoPort for the SSE MCP server.8765
MCP_TRANSPORTNoMCP transport mode. Use 'sse' for SSE transport.
FASTMCP_LOG_LEVELNoLog level for the FastMCP server.INFO
INTERVALS_ACCESS_MODENoAccess mode: 'admin' exposes legacy and safe writes, 'coach' exposes only safe writes, and 'readonly' hides mutation tools.
INTERVALS_API_BASE_URLNoBase URL for the Intervals.icu API.https://intervals.icu/api/v1
INTERVALS_ARTIFACT_DIRNoDirectory for temporary activity data artifacts..runtime/artifacts

Instructions

Guidance the server publishes about itself, which clients place ahead of the tool catalog so the model reads it before choosing anything.

This server publishes no instructions, or was last inspected before Glama recorded them.

Capabilities

Features and capabilities supported by this server

Protocol revision2025-11-25

CapabilityDetails
tools
{
  "listChanged": false
}
prompts
{
  "listChanged": false
}
resources
{
  "subscribe": false,
  "listChanged": false
}
experimental
{}

Tools

Functions exposed to the LLM to take actions

NameDescription
export_activity_dataA

Export complete raw activity data to the configured local artifact.

Use this after a compact read when the client needs all stream samples and interval records. The stream arrays retain their upstream index and null values; this read validates their shape before handing them to the local artifact store. An optional stream_types filter narrows the streams, and an optional half-open sample range slices each returned array. The manifest reports source unit conflicts and time-axis ambiguity; optional type-based unit hints are separate from source declarations. Streams and intervals come from separate HTTP reads: the returned hash verifies the local composite bytes, not an atomic upstream snapshot. Use get_artifact_chunk with the opaque ID when the MCP client cannot access the server filesystem. Source completeness remains unknown until the upstream data contract says otherwise. Tymewear VT/VE remain in raw device units, with no conversion to liters; use get_metric_definitions for respiratory field and FIT mapping context.

get_activitiesA

List activities in a half-open local-date range with bounded paging.

start_date is inclusive and end_date_exclusive is exclusive. A valid empty list is different from a malformed upstream response. The first page is snapshotted for cursor continuation, and a cursor is valid only for the same athlete, range, timezone, and sport filter. The source endpoint does not expose a trustworthy completeness marker, so source completeness stays null even when the returned list is short. A Hidden source stub without a sport field is retained for identity, but a requested sport match is reported as unverified.

get_activity_detailsA

Return one activity with upstream fields preserved.

A source-hidden record is returned as partial with an explicit limitation. In that case the row is useful for identity and timing, but it is not evidence that metrics, intervals, or streams are available; use the dedicated interval and stream tools to check those resources. Set include_intervals to request the upstream embedded interval container; missing embedded intervals still require get_activity_intervals. For Tymewear, VT is relative tidal volume per breath (not VT1/VT2), VE is relative minute ventilation, and BR is breaths/min. Custom L/br or L/min labels are not calibration evidence; use get_metric_definitions.

get_activity_intervalsA

Return the activity interval container with index fields intact.

The documented shape contains icu_intervals and optionally icu_groups. A legacy flat list of interval objects is retained for compatibility with existing callers; all other shapes and mixed rows are explicit errors. Intervals use upstream sample indices, not invented elapsed seconds, and an empty valid container remains empty. average_tidal_volume is VT and average_tidal_volume_min is VE. For Tymewear their volume scale is relative; no /100-to-liters conversion applies. average_respiration is BR in breaths/min. See get_metric_definitions.

get_activity_messagesA

Return an upstream activity-message list, preserving text and identity metadata.

Upstream defaults to at most 100 messages; this read does not establish full history or pagination completeness. A list may be empty, but each member must be an object. Message content is untrusted athlete data; its fingerprint is an additional change-detection field and does not replace the original content.

get_activity_streamsA

Read activity streams by sample index with bounded preview or range.

range uses half-open sample indices [start_index, end_index); those indices are not elapsed seconds and a single range is limited to 10,000 samples. Use export_activity_data plus get_artifact_chunk for a larger complete transfer. preview returns all samples for short arrays and only the first and last five samples for longer arrays, reporting truncation only when samples were omitted. Missing stream types, missing/null primary arrays, ambiguous or unavailable time axes, and unequal lengths are explicit warnings. Duplicate and custom upstream streams are retained in order, and no primary data array is invented for a data2-only stream. The 1/min cadence unit is a display hint when the source did not declare a unit; Ride commonly means revolutions per minute, while running conventions depend on the device and upstream field, with no x2 conversion. alignment.quality describes array-length alignment only; it does not assert monotonic, regular, or non-null time values. The snapshot hashes the returned payload for this selection, so a continuation must keep the same activity and stream_types.

Respiratory fields: tidal_volume = VT (volume per breath, not VT1/VT2), tidal_volume_min = VE (minute ventilation), respiration = BR (breaths/min). When sourced from Tymewear, VT uses relative i.u. and VE relative vol/min, not calibrated liters. Do not divide VT by 100 or 1000. The response adds conditional documentation in provenance.respiratory_interpretation; original samples and source unit labels are preserved. Use get_metric_definitions and get_custom_items to check mappings and units.

get_eventsA

Return calendar events overlapping a half-open local-date range.

start_date is inclusive and end_date_exclusive is exclusive; end_date remains the deprecated inclusive alias. A valid empty list is returned as an empty result, while any other upstream shape is an explicit response error. Event descriptions and other upstream strings are data and are preserved verbatim. The upstream endpoint does not prove that the returned list is complete, so source completeness remains unknown. Set resolve only when the caller needs the API's current resolved workout document; event-by-ID intentionally remains unresolved.

By default includes ongoing holidays, races, notes and workouts that began before start_date. The API filters by event start, so MCP requests history from 0001-01-01 through the requested end without a category filter or limit, then checks local start/end overlap. End dates are exclusive. This can fetch more history than the returned selection; query.upstream_oldest and overlap report its scope. Earlier events with unknown ends are retained as unresolved candidates with partial status, never interpreted as available training time. Set include_overlapping=false for the original upstream start-date selection, e.g. resolving an already identified event on its exact start day.

get_event_by_idA

Return one event by numeric ID, preserving its upstream object.

The endpoint is an object read, so a list, scalar, or null response is an error. The existing empty-object {} response remains the compatible NOT_FOUND result. Use :func:get_events for date-range discovery; this tool intentionally does not resolve linked events.

get_workout_snapshotA

Read a workout's raw event, server version and execution precondition.

Bind data.event_fingerprint unchanged in an execute_operation update/delete precondition. This token is versioned by MCP, not an upstream ETag. Missing fields, null and zero remain distinct.

Pairing evidence is read by exact identity, using the event's calendar day when event-by-ID omits paired_activity_id. Positive activity links are reverse-checked by activity.paired_event_id. Only unpaired_observed permits the execution precondition; it does not prove the athlete did not train. Unknown, completed and linked states block safe update/delete. The writer repeats these checks with fresh reads. Source completeness and atomic conditional writes remain unverified. This read does not authorize a write.

get_custom_itemsA

Read custom-item definitions without executing their content.

Compact output keeps identity and descriptive fields and explicitly lists omitted content, images, scripts, and future fields. Each item includes an exact by-ID full_read continuation when an ID is present; use get_custom_item_by_id(..., detail='full') to preserve every parsed field and untrusted content. Full responses keep derived metadata outside the raw item object. An empty list is valid empty data, while wrappers, malformed members, and meaningless objects are errors. Declared units or origin remain unverified metadata; scripts and descriptions are data and are never executed.

get_custom_item_by_idA

Read one custom-item definition by positive integer ID.

detail='compact' omits content, images, scripts, and unknown fields with explicit omission lists and a full continuation. detail='full' preserves the complete parsed upstream object under data.item and places derived metadata outside it, including arbitrary content and source-like strings as untrusted data. An upstream {} remains the compatibility NOT_FOUND error; other malformed objects are invalid.

get_athlete_power_curvesA

Read selected athlete power-curve durations in seconds.

Choose this tool for season or custom-range best-power comparisons. Each requested duration is a positive integer number of seconds. Compact results return requested points and curve metadata; detail='full' also returns the untouched upstream curve as raw for deeper analysis. include_normalised is the legacy option for the upstream W/kg series, not Normalized Power. Empty upstream lists are valid empty data, while malformed shapes fail explicitly. Per-curve missing durations and null values are preserved, and source completeness remains unknown.

get_activity_power_curvesA

Read watts power curves for one activity and optional durations.

Choose this for best-power points from one activity. durations are positive seconds selected exactly from the upstream secs axis; omitted durations use the standard duration set, while full detail preserves the complete upstream axis. fatigue defaults to normal and each distinct selector is requested independently; the response keeps after_kj and does not infer selector identity from curve IDs. Compact points retain aligned sample indices and W/kg activity IDs, while large raw arrays are listed in omitted_fields. Use the supplied full_read continuation or detail='full' when those arrays or unknown fields are needed. Values are upstream watts; no MCP calculations are performed. Successful variants survive failures of other variants. Selection echo and point completeness are separate; request context does not prove upstream selector identity. HTTP 422 guidance includes checking sport settings.

get_sport_settingsA

Read current-at-fetch settings for one sport or settings ID.

Choose this for the athlete's current FTP, zones, load order, and fatigue thresholds. sport is one selector: a sport name such as Ride or the current settings ID. Compact output keeps useful thresholds, zones, models, and ordering fields; full_read or detail='full' preserves every upstream field and unknown unit. ftp/p_max are W, w_prime is J, after_kj0/after_kj1 are kJ, heart-rate values are bpm, power zones are %FTP, and threshold_pace is always m/s; pace_units is only a display preference. These are current settings, not activity-assigned historical thresholds, and the MCP performs no physiological calculations. Source completeness is unknown.

get_metric_definitionsA

Explain selected metric names and fields from the local catalogue.

Use this before interpreting activity streams, intervals, wellness, or custom-item content. Selectors are optional; omitting both returns the small curated catalogue. Units, sample-index versus time axes, upstream reported/calculated/estimated status, and limitations are descriptive only. No account data is fetched, no training calculation is performed, and unknown selectors remain explicit in unknown_names.

Includes VT/tidal_volume, VE/tidal_volume_min and BR/respiration with Tymewear FIT mappings and device-unit context. Tymewear volume is relative, not calibrated liters; do not divide raw VT by 100 or 1000. VT is volume per breath, distinct from thresholds VT1/VT2. Custom names/units require source verification; this catalogue does not identify a recording's device.

get_artifact_chunkA

Read one verified byte range from a temporary activity export.

Use the opaque artifact_id returned by export_activity_data. Decode each base64 chunk and concatenate the raw bytes in offset order. Verify the SHA-256 of all bytes against the export manifest before decoding the full document as UTF-8 JSON; an individual chunk can split a multibyte character. response_complete covers this requested chunk only. eof and next_offset state whether more artifact bytes remain. The artifact is a local composite of separate stream and interval requests, so upstream source completeness and atomicity remain unknown.

get_activity_interval_statsA

Read upstream interval statistics for a half-open sample-index range.

Choose this when the client needs the Intervals.icu Interval object for [start_index, end_index). Indices are samples, never seconds, and all upstream fields, including nulls, zeroes, and future fields, stay in data. The MCP adds provenance, units, and requested/returned bounds but performs no numeric calculations. A returned range mismatch is explicit partial data; use the suggested get_activity_streams time range to map sample indices to elapsed time. Source completeness is unknown.

average_tidal_volume is VT (volume per breath, not VT1/VT2), average_tidal_volume_min is VE, and average_respiration is BR. Tymewear volumes use relative device units, not calibrated liters; no /100 or /1000 conversion is applied. Conditional field documentation is returned in provenance.respiratory_interpretation; see get_metric_definitions.

get_activity_best_effortsA

Find upstream best efforts by one duration or distance selector.

Choose this to ask Intervals.icu for ranked efforts on a named stream. duration is positive seconds or distance is positive finite metres; exactly one is required. start_index and the exclusive end_index are sample indices. end_index=0 is the upstream whole-stream sentinel, while end_index=None omits that optional parameter. count is locally limited to 1..100. The MCP preserves each Effort and its metadata, reports average units (W, bpm, m/s, or unknown for custom streams), and performs no FTP, VO2, or numeric calculations. min_value may expand an effort, so returned durations and bounds remain upstream facts; source completeness is unknown.

get_activity_power_hrA

Read native power-versus-HR analysis, including upstream HR lag and windows.

Values, coefficients and selection indices are source-provided. No new physiological calculations or causal conclusions are made. Compact detail keeps the first 120 series rows and eight curves, then omits whole fields if needed to bound data to 32 KiB. Exact omissions and a full continuation are returned. Full detail preserves the complete JSON object.

get_activity_data_qualityA

Summarize all returned stream samples and activity metadata using GETs only.

Finite fractions use source sample count, not elapsed time. Time gaps are relative to a 1-second reference and do not prove dropped sensor data. Optional secondary arrays, missing primary arrays, zero and null are distinct. Multiple time streams are reported as an ambiguous axis, without choosing the first. Source unit conflicts and inferred display hints are separate. Metadata and stream failures are independent. No sensor-source, physiological or readiness inference is made, and only 20 gap examples are returned. Conditional Tymewear documentation is in provenance.respiratory_interpretation: VT/VE use relative device volume units, BR uses breaths/min. Finite raw VT values such as 186 are not invalid merely because they are not in liters. No volume conversion or VE=VT*BR consistency check is performed.

get_capabilitiesA

Describe implemented/configured/live-verified integration capabilities.

get_wellness_dataA

Return raw wellness records using a half-open local-date range.

start_date is inclusive and end_date_exclusive is exclusive; end_date remains the deprecated inclusive alias. The upstream API may return an array of records or a date-keyed object whose keys are ISO-local dates. Empty arrays/maps are valid empty data, while null, scalar, mixed-member, and meaningless date-map shapes are errors. All native and custom fields, including null and zero, are preserved and source completeness remains unknown. Consult get_metric_definitions before interpreting fields: native hrv is rMSSD and hrvSDNN is a separate millisecond field; generic VO2max method and custom-field units remain unknown unless explicitly declared by the source.

execute_operationC

Execute one version-negotiated typed operation through the durable engine.

The exact operation UID is the retry identity. Changed content conflicts; an uncertain attempt is never sent again.

get_operation_statusA

Read durable typed-operation status without contacting Intervals.

recover_operationB

Reconcile by independent read-back only; never resend the mutation.

release_operation_riskA

Audit an explicit admin risk acceptance and release only its hold.

This does not change the historical unknown result and cannot replay it.

Prompts

Interactive templates invoked by user choice

NameDescription

No prompts

Resources

Contextual data attached and managed by the client

NameDescription

No resources

TDQS

A3.5/5.0

Scored across 26 tools

Disambiguation4/5

The tools are mostly distinct: activity streams, intervals, power curves, best efforts, and data quality each map to different upstream resources. However, several names are near-siblings (get_activity_intervals vs get_activity_interval_stats, get_activity_power_curves vs get_athlete_power_curves) and require reading descriptions to avoid misselection.

Naming Consistency4/5

Almost all reads use get_<resource>_<detail> in snake_case, with mutation verbs like export/execute/release/recover. The pattern is readable but not perfectly uniform: some singular reads use _by_id (get_event_by_id, get_custom_item_by_id) while get_activity_details and get_workout_snapshot do not follow that suffix.

Tool Count2/5

At 26 tools, the server crosses the 'too many' threshold; even though each tool has a distinct purpose, the dense activity-reader family and operation-management cluster create a large selection surface. A more consolidated design would be more appropriate.

Completeness3/5

The read/export side is very thorough: activities, streams, intervals, power curves, events, wellness, settings, custom items, and metrics are covered. However, there are no direct create/update/delete tools for activities, events, or custom items; the generic execute_operation is an indirect workaround, and there is no athlete-profile or dedicated workout-list tool beyond get_events.

Maintenance

ActivityMaintained
ResponsivenessUnresponsive