device and sourceRevision properties. At the Swift / in-memory layer those are references — one HKDevice instance for “My Apple Watch” can be shared by 5,000 samples. At the JSON layer there’s no concept of references, so a naive encoding inlines those structs per sample.
HealthKite MCP hoists provenance into a top-level sources array and replaces inline provenance with integer src indices.
Shape
Every response that contains sample-level data uses the samesources block:
src: Int referencing one of these entries. Sub-streams (e.g., samples inside a workout) can also carry per-sample src arrays when a stream’s samples come from multiple sources (rare).
Rules
id: 0is the primary source for the top-level object. For aWorkoutDetail, it’s the source that recorded the workout itself. For aSleepSession, it’s the source for the bulk of the session’s samples. For a quantity series, it’s the source of the first sample.- Tuple identity: a
SourceEntryis uniquely identified by(device, sourceRevision).device == nilis distinct from a present device, even when thesourceRevisionmatches. - Append order: entries appear in the order their tuples were first encountered while walking samples. The workout/session/series’ own source is always first.
- Omitted when uniform: per-sample
srcarrays inside columnar streams are omitted entirely if every sample in that stream hassrc == 0. This is the common case and keeps payloads small.
Why hoist
For a 33-minute run with 5,000 samples coming from a single Apple Watch, naive inlining duplicates 5,000 copies ofdevice + sourceRevision blocks. Hoisting drops payload size by ~70% in real measurements. The structure is also more honest: there’s one watch involved, so the JSON should reflect one watch entry.
Multi-source case
Many users get health data from more than one source — for example, Apple Watch for workouts and HR, and Eight Sleep for sleep tracking. A weekly snapshot response will have multiple entries insources. Each sample references the appropriate one by src. Consumers wanting to filter by source can do so with one integer comparison instead of struct equality.
Stability
Sourceid values are only stable within a single response. The same physical Apple Watch may be id: 0 in one response and id: 1 in another, depending on which sample types were queried and in what order. Always resolve src against the sources array in the same response — do not cache id mappings across requests.