feat(mcp): add a dedicated MCP measurement source for write provenance

Append an MCP value to MeasurementSource (migration 0196, additive enum
extension) so a row logged through the remote MCP endpoint is distinguishable
from MANUAL/TELEGRAM provenance and gets an isolated idempotency namespace
(userId, type, MCP, externalId). The value joins the read enum so existing
read/response shapes decode the rows; the REST batch and single-POST write
surfaces still reject it (server-resolved only). Regenerate the OpenAPI
contract for the additive enum.
This commit is contained in:
Marc-André Bombeck committed 2026-06-27 17:38:29 +02:00
1 parent cfffb693c3
commit 90c8768407
4 files changed
+146

No files matched your search

+116
View File
@@ -1745,6 +1745,7 @@ paths:
- POLAR
- OURA
- TELEGRAM
- MCP
- in: query
name: valueMin
schema:
@@ -2240,6 +2241,7 @@ paths:
- POLAR
- OURA
- TELEGRAM
- MCP
externalId:
type: string
maxLength: 128
@@ -3358,6 +3360,7 @@ paths:
- POLAR
- OURA
- TELEGRAM
- MCP
activeEnergy:
maxItems: 8
type: array
@@ -3375,6 +3378,7 @@ paths:
- POLAR
- OURA
- TELEGRAM
- MCP
walkingRunningDistance:
maxItems: 8
type: array
@@ -3392,6 +3396,7 @@ paths:
- POLAR
- OURA
- TELEGRAM
- MCP
flightsClimbed:
maxItems: 8
type: array
@@ -3409,6 +3414,7 @@ paths:
- POLAR
- OURA
- TELEGRAM
- MCP
sleep:
maxItems: 8
type: array
@@ -3426,6 +3432,7 @@ paths:
- POLAR
- OURA
- TELEGRAM
- MCP
weight:
maxItems: 8
type: array
@@ -3443,6 +3450,7 @@ paths:
- POLAR
- OURA
- TELEGRAM
- MCP
bloodPressure:
maxItems: 8
type: array
@@ -3460,6 +3468,7 @@ paths:
- POLAR
- OURA
- TELEGRAM
- MCP
pulse:
maxItems: 8
type: array
@@ -3477,6 +3486,7 @@ paths:
- POLAR
- OURA
- TELEGRAM
- MCP
bodyFat:
maxItems: 8
type: array
@@ -3494,6 +3504,7 @@ paths:
- POLAR
- OURA
- TELEGRAM
- MCP
bodyTemperature:
maxItems: 8
type: array
@@ -3511,6 +3522,7 @@ paths:
- POLAR
- OURA
- TELEGRAM
- MCP
spo2:
maxItems: 8
type: array
@@ -3528,6 +3540,7 @@ paths:
- POLAR
- OURA
- TELEGRAM
- MCP
hrv:
maxItems: 8
type: array
@@ -3545,6 +3558,7 @@ paths:
- POLAR
- OURA
- TELEGRAM
- MCP
restingHeartRate:
maxItems: 8
type: array
@@ -3562,6 +3576,7 @@ paths:
- POLAR
- OURA
- TELEGRAM
- MCP
vo2Max:
maxItems: 8
type: array
@@ -3579,6 +3594,7 @@ paths:
- POLAR
- OURA
- TELEGRAM
- MCP
skinTemperature:
maxItems: 8
type: array
@@ -3596,6 +3612,7 @@ paths:
- POLAR
- OURA
- TELEGRAM
- MCP
respiratoryRate:
maxItems: 8
type: array
@@ -3613,6 +3630,7 @@ paths:
- POLAR
- OURA
- TELEGRAM
- MCP
recovery:
maxItems: 8
type: array
@@ -3630,6 +3648,7 @@ paths:
- POLAR
- OURA
- TELEGRAM
- MCP
stress:
maxItems: 8
type: array
@@ -3647,6 +3666,7 @@ paths:
- POLAR
- OURA
- TELEGRAM
- MCP
metricPriority:
type: object
properties:
@@ -3667,6 +3687,7 @@ paths:
- POLAR
- OURA
- TELEGRAM
- MCP
activeEnergy:
maxItems: 8
type: array
@@ -3684,6 +3705,7 @@ paths:
- POLAR
- OURA
- TELEGRAM
- MCP
walkingRunningDistance:
maxItems: 8
type: array
@@ -3701,6 +3723,7 @@ paths:
- POLAR
- OURA
- TELEGRAM
- MCP
flightsClimbed:
maxItems: 8
type: array
@@ -3718,6 +3741,7 @@ paths:
- POLAR
- OURA
- TELEGRAM
- MCP
sleep:
maxItems: 8
type: array
@@ -3735,6 +3759,7 @@ paths:
- POLAR
- OURA
- TELEGRAM
- MCP
weight:
maxItems: 8
type: array
@@ -3752,6 +3777,7 @@ paths:
- POLAR
- OURA
- TELEGRAM
- MCP
bloodPressure:
maxItems: 8
type: array
@@ -3769,6 +3795,7 @@ paths:
- POLAR
- OURA
- TELEGRAM
- MCP
pulse:
maxItems: 8
type: array
@@ -3786,6 +3813,7 @@ paths:
- POLAR
- OURA
- TELEGRAM
- MCP
bodyFat:
maxItems: 8
type: array
@@ -3803,6 +3831,7 @@ paths:
- POLAR
- OURA
- TELEGRAM
- MCP
bodyTemperature:
maxItems: 8
type: array
@@ -3820,6 +3849,7 @@ paths:
- POLAR
- OURA
- TELEGRAM
- MCP
spo2:
maxItems: 8
type: array
@@ -3837,6 +3867,7 @@ paths:
- POLAR
- OURA
- TELEGRAM
- MCP
hrv:
maxItems: 8
type: array
@@ -3854,6 +3885,7 @@ paths:
- POLAR
- OURA
- TELEGRAM
- MCP
restingHeartRate:
maxItems: 8
type: array
@@ -3871,6 +3903,7 @@ paths:
- POLAR
- OURA
- TELEGRAM
- MCP
vo2Max:
maxItems: 8
type: array
@@ -3888,6 +3921,7 @@ paths:
- POLAR
- OURA
- TELEGRAM
- MCP
skinTemperature:
maxItems: 8
type: array
@@ -3905,6 +3939,7 @@ paths:
- POLAR
- OURA
- TELEGRAM
- MCP
respiratoryRate:
maxItems: 8
type: array
@@ -3922,6 +3957,7 @@ paths:
- POLAR
- OURA
- TELEGRAM
- MCP
recovery:
maxItems: 8
type: array
@@ -3939,6 +3975,7 @@ paths:
- POLAR
- OURA
- TELEGRAM
- MCP
stress:
maxItems: 8
type: array
@@ -3956,6 +3993,7 @@ paths:
- POLAR
- OURA
- TELEGRAM
- MCP
deviceTypePriority:
type: object
properties:
@@ -10743,6 +10781,7 @@ components:
- POLAR
- OURA
- TELEGRAM
- MCP
notes:
anyOf:
- type: string
@@ -11466,6 +11505,7 @@ components:
- POLAR
- OURA
- TELEGRAM
- MCP
notes:
anyOf:
- type: string
@@ -11709,6 +11749,7 @@ components:
- POLAR
- OURA
- TELEGRAM
- MCP
- type: "null"
start:
type: string
@@ -11836,6 +11877,7 @@ components:
- POLAR
- OURA
- TELEGRAM
- MCP
- type: "null"
start:
type: string
@@ -12209,6 +12251,7 @@ components:
- POLAR
- OURA
- TELEGRAM
- MCP
externalId:
anyOf:
- type: string
@@ -12318,6 +12361,7 @@ components:
- POLAR
- OURA
- TELEGRAM
- MCP
externalId:
anyOf:
- type: string
@@ -14529,6 +14573,7 @@ components:
- POLAR
- OURA
- TELEGRAM
- MCP
activeEnergy:
maxItems: 8
type: array
@@ -14546,6 +14591,7 @@ components:
- POLAR
- OURA
- TELEGRAM
- MCP
walkingRunningDistance:
maxItems: 8
type: array
@@ -14563,6 +14609,7 @@ components:
- POLAR
- OURA
- TELEGRAM
- MCP
flightsClimbed:
maxItems: 8
type: array
@@ -14580,6 +14627,7 @@ components:
- POLAR
- OURA
- TELEGRAM
- MCP
sleep:
maxItems: 8
type: array
@@ -14597,6 +14645,7 @@ components:
- POLAR
- OURA
- TELEGRAM
- MCP
weight:
maxItems: 8
type: array
@@ -14614,6 +14663,7 @@ components:
- POLAR
- OURA
- TELEGRAM
- MCP
bloodPressure:
maxItems: 8
type: array
@@ -14631,6 +14681,7 @@ components:
- POLAR
- OURA
- TELEGRAM
- MCP
pulse:
maxItems: 8
type: array
@@ -14648,6 +14699,7 @@ components:
- POLAR
- OURA
- TELEGRAM
- MCP
bodyFat:
maxItems: 8
type: array
@@ -14665,6 +14717,7 @@ components:
- POLAR
- OURA
- TELEGRAM
- MCP
bodyTemperature:
maxItems: 8
type: array
@@ -14682,6 +14735,7 @@ components:
- POLAR
- OURA
- TELEGRAM
- MCP
spo2:
maxItems: 8
type: array
@@ -14699,6 +14753,7 @@ components:
- POLAR
- OURA
- TELEGRAM
- MCP
hrv:
maxItems: 8
type: array
@@ -14716,6 +14771,7 @@ components:
- POLAR
- OURA
- TELEGRAM
- MCP
restingHeartRate:
maxItems: 8
type: array
@@ -14733,6 +14789,7 @@ components:
- POLAR
- OURA
- TELEGRAM
- MCP
vo2Max:
maxItems: 8
type: array
@@ -14750,6 +14807,7 @@ components:
- POLAR
- OURA
- TELEGRAM
- MCP
skinTemperature:
maxItems: 8
type: array
@@ -14767,6 +14825,7 @@ components:
- POLAR
- OURA
- TELEGRAM
- MCP
respiratoryRate:
maxItems: 8
type: array
@@ -14784,6 +14843,7 @@ components:
- POLAR
- OURA
- TELEGRAM
- MCP
recovery:
maxItems: 8
type: array
@@ -14801,6 +14861,7 @@ components:
- POLAR
- OURA
- TELEGRAM
- MCP
stress:
maxItems: 8
type: array
@@ -14818,6 +14879,7 @@ components:
- POLAR
- OURA
- TELEGRAM
- MCP
metricPriority:
type: object
properties:
@@ -14838,6 +14900,7 @@ components:
- POLAR
- OURA
- TELEGRAM
- MCP
activeEnergy:
maxItems: 8
type: array
@@ -14855,6 +14918,7 @@ components:
- POLAR
- OURA
- TELEGRAM
- MCP
walkingRunningDistance:
maxItems: 8
type: array
@@ -14872,6 +14936,7 @@ components:
- POLAR
- OURA
- TELEGRAM
- MCP
flightsClimbed:
maxItems: 8
type: array
@@ -14889,6 +14954,7 @@ components:
- POLAR
- OURA
- TELEGRAM
- MCP
sleep:
maxItems: 8
type: array
@@ -14906,6 +14972,7 @@ components:
- POLAR
- OURA
- TELEGRAM
- MCP
weight:
maxItems: 8
type: array
@@ -14923,6 +14990,7 @@ components:
- POLAR
- OURA
- TELEGRAM
- MCP
bloodPressure:
maxItems: 8
type: array
@@ -14940,6 +15008,7 @@ components:
- POLAR
- OURA
- TELEGRAM
- MCP
pulse:
maxItems: 8
type: array
@@ -14957,6 +15026,7 @@ components:
- POLAR
- OURA
- TELEGRAM
- MCP
bodyFat:
maxItems: 8
type: array
@@ -14974,6 +15044,7 @@ components:
- POLAR
- OURA
- TELEGRAM
- MCP
bodyTemperature:
maxItems: 8
type: array
@@ -14991,6 +15062,7 @@ components:
- POLAR
- OURA
- TELEGRAM
- MCP
spo2:
maxItems: 8
type: array
@@ -15008,6 +15080,7 @@ components:
- POLAR
- OURA
- TELEGRAM
- MCP
hrv:
maxItems: 8
type: array
@@ -15025,6 +15098,7 @@ components:
- POLAR
- OURA
- TELEGRAM
- MCP
restingHeartRate:
maxItems: 8
type: array
@@ -15042,6 +15116,7 @@ components:
- POLAR
- OURA
- TELEGRAM
- MCP
vo2Max:
maxItems: 8
type: array
@@ -15059,6 +15134,7 @@ components:
- POLAR
- OURA
- TELEGRAM
- MCP
skinTemperature:
maxItems: 8
type: array
@@ -15076,6 +15152,7 @@ components:
- POLAR
- OURA
- TELEGRAM
- MCP
respiratoryRate:
maxItems: 8
type: array
@@ -15093,6 +15170,7 @@ components:
- POLAR
- OURA
- TELEGRAM
- MCP
recovery:
maxItems: 8
type: array
@@ -15110,6 +15188,7 @@ components:
- POLAR
- OURA
- TELEGRAM
- MCP
stress:
maxItems: 8
type: array
@@ -15127,6 +15206,7 @@ components:
- POLAR
- OURA
- TELEGRAM
- MCP
additionalProperties: false
deviceTypePriority:
type: object
@@ -15193,6 +15273,7 @@ components:
- POLAR
- OURA
- TELEGRAM
- MCP
activeEnergy:
maxItems: 8
type: array
@@ -15210,6 +15291,7 @@ components:
- POLAR
- OURA
- TELEGRAM
- MCP
walkingRunningDistance:
maxItems: 8
type: array
@@ -15227,6 +15309,7 @@ components:
- POLAR
- OURA
- TELEGRAM
- MCP
flightsClimbed:
maxItems: 8
type: array
@@ -15244,6 +15327,7 @@ components:
- POLAR
- OURA
- TELEGRAM
- MCP
sleep:
maxItems: 8
type: array
@@ -15261,6 +15345,7 @@ components:
- POLAR
- OURA
- TELEGRAM
- MCP
weight:
maxItems: 8
type: array
@@ -15278,6 +15363,7 @@ components:
- POLAR
- OURA
- TELEGRAM
- MCP
bloodPressure:
maxItems: 8
type: array
@@ -15295,6 +15381,7 @@ components:
- POLAR
- OURA
- TELEGRAM
- MCP
pulse:
maxItems: 8
type: array
@@ -15312,6 +15399,7 @@ components:
- POLAR
- OURA
- TELEGRAM
- MCP
bodyFat:
maxItems: 8
type: array
@@ -15329,6 +15417,7 @@ components:
- POLAR
- OURA
- TELEGRAM
- MCP
bodyTemperature:
maxItems: 8
type: array
@@ -15346,6 +15435,7 @@ components:
- POLAR
- OURA
- TELEGRAM
- MCP
spo2:
maxItems: 8
type: array
@@ -15363,6 +15453,7 @@ components:
- POLAR
- OURA
- TELEGRAM
- MCP
hrv:
maxItems: 8
type: array
@@ -15380,6 +15471,7 @@ components:
- POLAR
- OURA
- TELEGRAM
- MCP
restingHeartRate:
maxItems: 8
type: array
@@ -15397,6 +15489,7 @@ components:
- POLAR
- OURA
- TELEGRAM
- MCP
vo2Max:
maxItems: 8
type: array
@@ -15414,6 +15507,7 @@ components:
- POLAR
- OURA
- TELEGRAM
- MCP
skinTemperature:
maxItems: 8
type: array
@@ -15431,6 +15525,7 @@ components:
- POLAR
- OURA
- TELEGRAM
- MCP
respiratoryRate:
maxItems: 8
type: array
@@ -15448,6 +15543,7 @@ components:
- POLAR
- OURA
- TELEGRAM
- MCP
recovery:
maxItems: 8
type: array
@@ -15465,6 +15561,7 @@ components:
- POLAR
- OURA
- TELEGRAM
- MCP
stress:
maxItems: 8
type: array
@@ -15482,6 +15579,7 @@ components:
- POLAR
- OURA
- TELEGRAM
- MCP
metricPriority:
type: object
properties:
@@ -15502,6 +15600,7 @@ components:
- POLAR
- OURA
- TELEGRAM
- MCP
activeEnergy:
maxItems: 8
type: array
@@ -15519,6 +15618,7 @@ components:
- POLAR
- OURA
- TELEGRAM
- MCP
walkingRunningDistance:
maxItems: 8
type: array
@@ -15536,6 +15636,7 @@ components:
- POLAR
- OURA
- TELEGRAM
- MCP
flightsClimbed:
maxItems: 8
type: array
@@ -15553,6 +15654,7 @@ components:
- POLAR
- OURA
- TELEGRAM
- MCP
sleep:
maxItems: 8
type: array
@@ -15570,6 +15672,7 @@ components:
- POLAR
- OURA
- TELEGRAM
- MCP
weight:
maxItems: 8
type: array
@@ -15587,6 +15690,7 @@ components:
- POLAR
- OURA
- TELEGRAM
- MCP
bloodPressure:
maxItems: 8
type: array
@@ -15604,6 +15708,7 @@ components:
- POLAR
- OURA
- TELEGRAM
- MCP
pulse:
maxItems: 8
type: array
@@ -15621,6 +15726,7 @@ components:
- POLAR
- OURA
- TELEGRAM
- MCP
bodyFat:
maxItems: 8
type: array
@@ -15638,6 +15744,7 @@ components:
- POLAR
- OURA
- TELEGRAM
- MCP
bodyTemperature:
maxItems: 8
type: array
@@ -15655,6 +15762,7 @@ components:
- POLAR
- OURA
- TELEGRAM
- MCP
spo2:
maxItems: 8
type: array
@@ -15672,6 +15780,7 @@ components:
- POLAR
- OURA
- TELEGRAM
- MCP
hrv:
maxItems: 8
type: array
@@ -15689,6 +15798,7 @@ components:
- POLAR
- OURA
- TELEGRAM
- MCP
restingHeartRate:
maxItems: 8
type: array
@@ -15706,6 +15816,7 @@ components:
- POLAR
- OURA
- TELEGRAM
- MCP
vo2Max:
maxItems: 8
type: array
@@ -15723,6 +15834,7 @@ components:
- POLAR
- OURA
- TELEGRAM
- MCP
skinTemperature:
maxItems: 8
type: array
@@ -15740,6 +15852,7 @@ components:
- POLAR
- OURA
- TELEGRAM
- MCP
respiratoryRate:
maxItems: 8
type: array
@@ -15757,6 +15870,7 @@ components:
- POLAR
- OURA
- TELEGRAM
- MCP
recovery:
maxItems: 8
type: array
@@ -15774,6 +15888,7 @@ components:
- POLAR
- OURA
- TELEGRAM
- MCP
stress:
maxItems: 8
type: array
@@ -15791,6 +15906,7 @@ components:
- POLAR
- OURA
- TELEGRAM
- MCP
additionalProperties: false
deviceTypePriority:
type: object
@@ -0,0 +1,14 @@
-- MCP write provenance.
--
-- The remote MCP endpoint gains a confirmed write surface (logging a
-- measurement / mood). Rows it writes carry their own `MeasurementSource`
-- value so they are distinguishable from MANUAL / TELEGRAM provenance and
-- get an isolated idempotency namespace `(userId, type, MCP, externalId)`.
--
-- Purely-additive enum extension; no row touched. `ADD VALUE IF NOT EXISTS`
-- makes a rerun safe. The new value is NOT used elsewhere in this migration,
-- so it is safe to extend the enum in the same step (Postgres only forbids
-- USING a freshly-added value in the same transaction). Mirrors the 0189
-- source-extension that added TELEGRAM. The enum is `@@map`-ed to the
-- snake_case type name `measurement_source`.
ALTER TYPE "measurement_source" ADD VALUE IF NOT EXISTS 'MCP';
+9
View File
@@ -1033,6 +1033,15 @@ enum MeasurementSource {
// (server-resolved only); iOS DTO mirrors this spelling so the
// /api/sync/changes delta feed decodes the rows.
TELEGRAM
// v1.22.0 — measurements logged through the remote MCP write surface. A
// distinct provenance from MANUAL/TELEGRAM: the row was confirmed and
// written by an external assistant over the `/mcp` wire under a
// `health:write`-scoped token, never the cookie/Bearer client write path.
// Its own value gives the MCP writes an isolated idempotency namespace
// `(userId, type, MCP, externalId)`. The client batch + single-POST
// surfaces still reject it (server-resolved only); iOS DTO mirrors this
// spelling so the /api/sync/changes delta feed decodes the rows.
MCP
@@map("measurement_source")
}
+7
View File
@@ -184,6 +184,13 @@ export const measurementSourceEnum = z.enum([
// decode the rows it surfaces; the client-facing write surfaces still
// reject it (see `WRITABLE_MEASUREMENT_SOURCES` + the batch allowlist).
"TELEGRAM",
// v1.22.0 — measurements logged through the confirmed remote MCP write
// surface under a `health:write`-scoped token. Server-resolved (the row is
// written in-process over `/mcp`, never through the cookie/Bearer client
// write path), so it is excluded from `WRITABLE_MEASUREMENT_SOURCES`; part
// of this enum so the read/response shapes (and the iOS decoder) can decode
// the rows it surfaces.
"MCP",
]);
/**