@zykeco/sync-server 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/app.d.ts ADDED
@@ -0,0 +1,4 @@
1
+ import { Hono } from 'hono';
2
+ import type { Db } from './db/client.js';
3
+ import type { Env } from './env.js';
4
+ export declare function buildApp(db: Db, env: Env): Hono;
package/dist/app.js ADDED
@@ -0,0 +1,56 @@
1
+ import { Hono } from 'hono';
2
+ import { dailyMetricsRoutes } from './routes/daily-metrics.js';
3
+ import { dailyStressBurdenRoutes } from './routes/daily-stress-burden.js';
4
+ import { healthRoutes } from './routes/health.js';
5
+ import { heartRateMinuteRoutes } from './routes/heart-rate-minute.js';
6
+ import { hrZoneHistoryRoutes } from './routes/hr-zone-history.js';
7
+ import { mcpRoutes } from './routes/mcp.js';
8
+ import { sleepSessionsRoutes } from './routes/sleep-sessions.js';
9
+ import { userProfileRoutes } from './routes/user-profile.js';
10
+ import { userTimezonesRoutes } from './routes/user-timezones.js';
11
+ import { weeklyMetricsRoutes } from './routes/weekly-metrics.js';
12
+ import { wipeRoutes } from './routes/wipe.js';
13
+ import { createDailyMetricsStore } from './stores/daily-metrics.js';
14
+ import { createDailyStressBurdenStore } from './stores/daily-stress-burden.js';
15
+ import { createHeartRateMinuteStore } from './stores/heart-rate-minute.js';
16
+ import { createHrZoneHistoryStore } from './stores/hr-zone-history.js';
17
+ import { createSleepSessionsStore } from './stores/sleep-sessions.js';
18
+ import { createUserProfileStore } from './stores/user-profile.js';
19
+ import { createUserTimezonesStore } from './stores/user-timezones.js';
20
+ import { createWeeklyMetricsStore } from './stores/weekly-metrics.js';
21
+ export function buildApp(db, env) {
22
+ const auth = { readSecret: env.readSecret, writeSecret: env.writeSecret };
23
+ const app = new Hono();
24
+ app.route('/v1/health', healthRoutes(db));
25
+ const dailyMetricsStore = createDailyMetricsStore(db);
26
+ const weeklyMetricsStore = createWeeklyMetricsStore(db);
27
+ const sleepSessionsStore = createSleepSessionsStore(db);
28
+ const userTimezonesStore = createUserTimezonesStore(db);
29
+ const userProfileStore = createUserProfileStore(db);
30
+ const dailyStressBurdenStore = createDailyStressBurdenStore(db);
31
+ const heartRateMinuteStore = createHeartRateMinuteStore(db);
32
+ const hrZoneHistoryStore = createHrZoneHistoryStore(db);
33
+ app.route('/v1/sync/daily-metrics', dailyMetricsRoutes(dailyMetricsStore, auth));
34
+ app.route('/v1/sync/weekly-metrics', weeklyMetricsRoutes(weeklyMetricsStore, auth));
35
+ app.route('/v1/sync/sleep-sessions', sleepSessionsRoutes(sleepSessionsStore, auth));
36
+ app.route('/v1/sync/user-timezones', userTimezonesRoutes(userTimezonesStore, auth));
37
+ app.route('/v1/sync/user-profile', userProfileRoutes(userProfileStore, auth));
38
+ app.route('/v1/sync/daily-stress-burden', dailyStressBurdenRoutes(dailyStressBurdenStore, auth));
39
+ app.route('/v1/sync/heart-rate-minute', heartRateMinuteRoutes(heartRateMinuteStore, auth));
40
+ app.route('/v1/sync/hr-zone-history', hrZoneHistoryRoutes(hrZoneHistoryStore, auth));
41
+ app.route('/v1/sync/wipe', wipeRoutes({
42
+ dailyMetrics: dailyMetricsStore,
43
+ weeklyMetrics: weeklyMetricsStore,
44
+ sleepSessions: sleepSessionsStore,
45
+ userTimezones: userTimezonesStore,
46
+ userProfile: userProfileStore,
47
+ dailyStressBurden: dailyStressBurdenStore,
48
+ heartRateMinute: heartRateMinuteStore,
49
+ hrZoneHistory: hrZoneHistoryStore,
50
+ }, auth));
51
+ if (env.mcp) {
52
+ app.route('/mcp', mcpRoutes({ db, env, mcp: env.mcp }));
53
+ }
54
+ return app;
55
+ }
56
+ //# sourceMappingURL=app.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app.js","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"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"}
package/dist/env.d.ts CHANGED
@@ -4,5 +4,11 @@ export interface Env {
4
4
  databaseUrl: string;
5
5
  readSecret: string;
6
6
  writeSecret: string;
7
+ mcp: McpEnv | null;
8
+ }
9
+ export interface McpEnv {
10
+ clientId: string;
11
+ tokenSecret: string;
12
+ tokenTtlSeconds: number;
7
13
  }
8
14
  export declare function loadEnv(): Env;
package/dist/env.js CHANGED
@@ -4,7 +4,15 @@ export function loadEnv() {
4
4
  const databaseUrl = process.env.DATABASE_URL ?? 'file:./data/sync.db';
5
5
  const readSecret = required('READ_SECRET');
6
6
  const writeSecret = required('WRITE_SECRET');
7
- return { port, databaseUrl, readSecret, writeSecret };
7
+ const mcpEnabled = (process.env.MCP_ENABLED ?? 'true').toLowerCase() !== 'false';
8
+ const mcp = mcpEnabled
9
+ ? {
10
+ clientId: required('MCP_CLIENT_ID'),
11
+ tokenSecret: required('MCP_TOKEN_SECRET'),
12
+ tokenTtlSeconds: Number(process.env.MCP_TOKEN_TTL_SECONDS ?? 3600),
13
+ }
14
+ : null;
15
+ return { port, databaseUrl, readSecret, writeSecret, mcp };
8
16
  }
9
17
  function required(name) {
10
18
  const value = process.env[name];
package/dist/env.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"env.js","sourceRoot":"","sources":["../src/env.ts"],"names":[],"mappings":"AAAA,OAAO,eAAe,CAAC;AASvB,MAAM,UAAU,OAAO;IACrB,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,IAAI,CAAC,CAAC;IAC9C,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,qBAAqB,CAAC;IACtE,MAAM,UAAU,GAAG,QAAQ,CAAC,aAAa,CAAC,CAAC;IAC3C,MAAM,WAAW,GAAG,QAAQ,CAAC,cAAc,CAAC,CAAC;IAC7C,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,UAAU,EAAE,WAAW,EAAE,CAAC;AACxD,CAAC;AAED,SAAS,QAAQ,CAAC,IAAY;IAC5B,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAChC,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CAAC,6BAA6B,IAAI,EAAE,CAAC,CAAC;IACvD,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
1
+ {"version":3,"file":"env.js","sourceRoot":"","sources":["../src/env.ts"],"names":[],"mappings":"AAAA,OAAO,eAAe,CAAC;AAgBvB,MAAM,UAAU,OAAO;IACrB,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,IAAI,CAAC,CAAC;IAC9C,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,qBAAqB,CAAC;IACtE,MAAM,UAAU,GAAG,QAAQ,CAAC,aAAa,CAAC,CAAC;IAC3C,MAAM,WAAW,GAAG,QAAQ,CAAC,cAAc,CAAC,CAAC;IAE7C,MAAM,UAAU,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,MAAM,CAAC,CAAC,WAAW,EAAE,KAAK,OAAO,CAAC;IACjF,MAAM,GAAG,GAAG,UAAU;QACpB,CAAC,CAAC;YACE,QAAQ,EAAE,QAAQ,CAAC,eAAe,CAAC;YACnC,WAAW,EAAE,QAAQ,CAAC,kBAAkB,CAAC;YACzC,eAAe,EAAE,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,qBAAqB,IAAI,IAAI,CAAC;SACnE;QACH,CAAC,CAAC,IAAI,CAAC;IAET,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,UAAU,EAAE,WAAW,EAAE,GAAG,EAAE,CAAC;AAC7D,CAAC;AAED,SAAS,QAAQ,CAAC,IAAY;IAC5B,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAChC,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CAAC,6BAA6B,IAAI,EAAE,CAAC,CAAC;IACvD,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
package/dist/index.js CHANGED
@@ -1,58 +1,12 @@
1
1
  import { serve } from '@hono/node-server';
2
- import { Hono } from 'hono';
2
+ import { buildApp } from './app.js';
3
3
  import { createDb } from './db/client.js';
4
4
  import { runMigrations } from './db/migrate.js';
5
5
  import { loadEnv } from './env.js';
6
- import { dailyMetricsRoutes } from './routes/daily-metrics.js';
7
- import { dailyStressBurdenRoutes } from './routes/daily-stress-burden.js';
8
- import { healthRoutes } from './routes/health.js';
9
- import { heartRateMinuteRoutes } from './routes/heart-rate-minute.js';
10
- import { hrZoneHistoryRoutes } from './routes/hr-zone-history.js';
11
- import { sleepSessionsRoutes } from './routes/sleep-sessions.js';
12
- import { userProfileRoutes } from './routes/user-profile.js';
13
- import { userTimezonesRoutes } from './routes/user-timezones.js';
14
- import { weeklyMetricsRoutes } from './routes/weekly-metrics.js';
15
- import { wipeRoutes } from './routes/wipe.js';
16
- import { createDailyMetricsStore } from './stores/daily-metrics.js';
17
- import { createDailyStressBurdenStore } from './stores/daily-stress-burden.js';
18
- import { createHeartRateMinuteStore } from './stores/heart-rate-minute.js';
19
- import { createHrZoneHistoryStore } from './stores/hr-zone-history.js';
20
- import { createSleepSessionsStore } from './stores/sleep-sessions.js';
21
- import { createUserProfileStore } from './stores/user-profile.js';
22
- import { createUserTimezonesStore } from './stores/user-timezones.js';
23
- import { createWeeklyMetricsStore } from './stores/weekly-metrics.js';
24
6
  const env = loadEnv();
25
7
  const db = createDb(env.databaseUrl);
26
8
  await runMigrations(db);
27
- const auth = { readSecret: env.readSecret, writeSecret: env.writeSecret };
28
- const app = new Hono();
29
- app.route('/v1/health', healthRoutes(db));
30
- const dailyMetricsStore = createDailyMetricsStore(db);
31
- const weeklyMetricsStore = createWeeklyMetricsStore(db);
32
- const sleepSessionsStore = createSleepSessionsStore(db);
33
- const userTimezonesStore = createUserTimezonesStore(db);
34
- const userProfileStore = createUserProfileStore(db);
35
- const dailyStressBurdenStore = createDailyStressBurdenStore(db);
36
- const heartRateMinuteStore = createHeartRateMinuteStore(db);
37
- const hrZoneHistoryStore = createHrZoneHistoryStore(db);
38
- app.route('/v1/sync/daily-metrics', dailyMetricsRoutes(dailyMetricsStore, auth));
39
- app.route('/v1/sync/weekly-metrics', weeklyMetricsRoutes(weeklyMetricsStore, auth));
40
- app.route('/v1/sync/sleep-sessions', sleepSessionsRoutes(sleepSessionsStore, auth));
41
- app.route('/v1/sync/user-timezones', userTimezonesRoutes(userTimezonesStore, auth));
42
- app.route('/v1/sync/user-profile', userProfileRoutes(userProfileStore, auth));
43
- app.route('/v1/sync/daily-stress-burden', dailyStressBurdenRoutes(dailyStressBurdenStore, auth));
44
- app.route('/v1/sync/heart-rate-minute', heartRateMinuteRoutes(heartRateMinuteStore, auth));
45
- app.route('/v1/sync/hr-zone-history', hrZoneHistoryRoutes(hrZoneHistoryStore, auth));
46
- app.route('/v1/sync/wipe', wipeRoutes({
47
- dailyMetrics: dailyMetricsStore,
48
- weeklyMetrics: weeklyMetricsStore,
49
- sleepSessions: sleepSessionsStore,
50
- userTimezones: userTimezonesStore,
51
- userProfile: userProfileStore,
52
- dailyStressBurden: dailyStressBurdenStore,
53
- heartRateMinute: heartRateMinuteStore,
54
- hrZoneHistory: hrZoneHistoryStore,
55
- }, auth));
9
+ const app = buildApp(db, env);
56
10
  serve({ fetch: app.fetch, port: env.port }, (info) => {
57
11
  console.info(`zyke-sync listening on http://localhost:${info.port}`);
58
12
  });
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAE1C,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AACpC,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AAEnC,MAAM,GAAG,GAAG,OAAO,EAAE,CAAC;AACtB,MAAM,EAAE,GAAG,QAAQ,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;AAErC,MAAM,aAAa,CAAC,EAAE,CAAC,CAAC;AAExB,MAAM,GAAG,GAAG,QAAQ,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;AAE9B,KAAK,CAAC,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,EAAE;IACnD,OAAO,CAAC,IAAI,CAAC,2CAA2C,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;AACvE,CAAC,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export type MetricValueType = 'seconds' | 'minutes' | 'milliseconds' | 'bpm' | 'percent' | 'ratio' | 'score' | 'count' | 'kcal' | 'meters' | 'kilograms' | 'celsius' | 'unknown';
2
+ export declare function inferValueType(metricKey: string): MetricValueType;
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Hand-curated overrides for metric keys whose name doesn't follow the suffix
3
+ * convention. Add entries here as we learn about ambiguous keys. The suffix
4
+ * inference below handles the common case.
5
+ */
6
+ const OVERRIDES = {
7
+ steps: 'count',
8
+ active_calories: 'kcal',
9
+ total_calories: 'kcal',
10
+ vo2_max: 'score',
11
+ weight: 'kilograms',
12
+ height: 'meters',
13
+ sleep_efficiency: 'ratio',
14
+ };
15
+ // Order matters — first match wins, so place longer/more specific suffixes first.
16
+ const SUFFIX_RULES = [
17
+ { suffix: '_ms', type: 'milliseconds' },
18
+ { suffix: '_minutes', type: 'minutes' },
19
+ { suffix: '_min', type: 'minutes' },
20
+ { suffix: '_s', type: 'seconds' },
21
+ { suffix: '_bpm', type: 'bpm' },
22
+ { suffix: '_percent', type: 'percent' },
23
+ { suffix: '_pct', type: 'percent' },
24
+ { suffix: '_ratio', type: 'ratio' },
25
+ { suffix: '_score', type: 'score' },
26
+ { suffix: '_count', type: 'count' },
27
+ { suffix: '_kcal', type: 'kcal' },
28
+ { suffix: '_kg', type: 'kilograms' },
29
+ { suffix: '_m', type: 'meters' },
30
+ { suffix: '_c', type: 'celsius' },
31
+ ];
32
+ export function inferValueType(metricKey) {
33
+ const override = OVERRIDES[metricKey];
34
+ if (override)
35
+ return override;
36
+ for (const rule of SUFFIX_RULES) {
37
+ if (metricKey.endsWith(rule.suffix))
38
+ return rule.type;
39
+ }
40
+ return 'unknown';
41
+ }
42
+ //# sourceMappingURL=metric-types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"metric-types.js","sourceRoot":"","sources":["../../src/mcp/metric-types.ts"],"names":[],"mappings":"AAeA;;;;GAIG;AACH,MAAM,SAAS,GAAoC;IACjD,KAAK,EAAE,OAAO;IACd,eAAe,EAAE,MAAM;IACvB,cAAc,EAAE,MAAM;IACtB,OAAO,EAAE,OAAO;IAChB,MAAM,EAAE,WAAW;IACnB,MAAM,EAAE,QAAQ;IAChB,gBAAgB,EAAE,OAAO;CAC1B,CAAC;AAOF,kFAAkF;AAClF,MAAM,YAAY,GAAiB;IACjC,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,cAAc,EAAE;IACvC,EAAE,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,SAAS,EAAE;IACvC,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE;IACnC,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE;IACjC,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE;IAC/B,EAAE,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,SAAS,EAAE;IACvC,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE;IACnC,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,OAAO,EAAE;IACnC,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,OAAO,EAAE;IACnC,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,OAAO,EAAE;IACnC,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE;IACjC,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,WAAW,EAAE;IACpC,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE;IAChC,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE;CAClC,CAAC;AAEF,MAAM,UAAU,cAAc,CAAC,SAAiB;IAC9C,MAAM,QAAQ,GAAG,SAAS,CAAC,SAAS,CAAC,CAAC;IACtC,IAAI,QAAQ;QAAE,OAAO,QAAQ,CAAC;IAC9B,KAAK,MAAM,IAAI,IAAI,YAAY,EAAE,CAAC;QAChC,IAAI,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC;YAAE,OAAO,IAAI,CAAC,IAAI,CAAC;IACxD,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC"}
@@ -0,0 +1,41 @@
1
+ import type { McpEnv } from '../env.js';
2
+ export interface TokenIssueResult {
3
+ accessToken: string;
4
+ expiresIn: number;
5
+ }
6
+ export type TokenVerifyResult = {
7
+ ok: true;
8
+ expiresAt: number;
9
+ } | {
10
+ ok: false;
11
+ reason: 'malformed' | 'bad_signature' | 'expired';
12
+ };
13
+ /**
14
+ * Issue an opaque, HMAC-signed bearer token.
15
+ *
16
+ * Token layout: `${b64url(payload)}.${hex(hmac_sha256(payload, secret))}`
17
+ * where `payload = random(16) || expiryUnixSeconds(uint64BE)`.
18
+ *
19
+ * Stateless — verification needs only the signing secret.
20
+ */
21
+ export declare function issueToken(env: McpEnv, nowMs?: number): TokenIssueResult;
22
+ export declare function verifyToken(token: string, env: McpEnv, nowMs?: number): TokenVerifyResult;
23
+ export interface ClientCredentialsRequest {
24
+ clientId: string;
25
+ clientSecret: string;
26
+ grantType: string;
27
+ }
28
+ export type GrantResult = {
29
+ ok: true;
30
+ token: TokenIssueResult;
31
+ } | {
32
+ ok: false;
33
+ status: number;
34
+ error: string;
35
+ description?: string;
36
+ };
37
+ /**
38
+ * Validate a client_credentials grant request against the configured MCP env
39
+ * + the read secret. Returns the access token on success.
40
+ */
41
+ export declare function grantClientCredentials(req: ClientCredentialsRequest, env: McpEnv, readSecret: string, nowMs?: number): GrantResult;
@@ -0,0 +1,90 @@
1
+ import { createHmac, randomBytes, timingSafeEqual } from 'node:crypto';
2
+ const PAYLOAD_RANDOM_BYTES = 16;
3
+ const PAYLOAD_EXPIRY_BYTES = 8;
4
+ const PAYLOAD_BYTES = PAYLOAD_RANDOM_BYTES + PAYLOAD_EXPIRY_BYTES;
5
+ const SIG_HEX_LEN = 64; // sha256 hex = 64 chars
6
+ function sign(payload, secret) {
7
+ return createHmac('sha256', secret).update(payload).digest('hex');
8
+ }
9
+ function b64urlEncode(buf) {
10
+ return buf.toString('base64').replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
11
+ }
12
+ function b64urlDecode(s) {
13
+ try {
14
+ const pad = s.length % 4 === 0 ? '' : '='.repeat(4 - (s.length % 4));
15
+ return Buffer.from(s.replace(/-/g, '+').replace(/_/g, '/') + pad, 'base64');
16
+ }
17
+ catch {
18
+ return null;
19
+ }
20
+ }
21
+ /**
22
+ * Issue an opaque, HMAC-signed bearer token.
23
+ *
24
+ * Token layout: `${b64url(payload)}.${hex(hmac_sha256(payload, secret))}`
25
+ * where `payload = random(16) || expiryUnixSeconds(uint64BE)`.
26
+ *
27
+ * Stateless — verification needs only the signing secret.
28
+ */
29
+ export function issueToken(env, nowMs = Date.now()) {
30
+ const expiresAt = Math.floor(nowMs / 1000) + env.tokenTtlSeconds;
31
+ const payload = Buffer.alloc(PAYLOAD_BYTES);
32
+ randomBytes(PAYLOAD_RANDOM_BYTES).copy(payload, 0);
33
+ payload.writeBigUInt64BE(BigInt(expiresAt), PAYLOAD_RANDOM_BYTES);
34
+ const sig = sign(payload, env.tokenSecret);
35
+ return {
36
+ accessToken: `${b64urlEncode(payload)}.${sig}`,
37
+ expiresIn: env.tokenTtlSeconds,
38
+ };
39
+ }
40
+ export function verifyToken(token, env, nowMs = Date.now()) {
41
+ const dot = token.indexOf('.');
42
+ if (dot <= 0)
43
+ return { ok: false, reason: 'malformed' };
44
+ const payloadB64 = token.slice(0, dot);
45
+ const sigHex = token.slice(dot + 1);
46
+ if (sigHex.length !== SIG_HEX_LEN)
47
+ return { ok: false, reason: 'malformed' };
48
+ const payload = b64urlDecode(payloadB64);
49
+ if (!payload || payload.length !== PAYLOAD_BYTES)
50
+ return { ok: false, reason: 'malformed' };
51
+ const expectedSigHex = sign(payload, env.tokenSecret);
52
+ const a = Buffer.from(sigHex, 'hex');
53
+ const b = Buffer.from(expectedSigHex, 'hex');
54
+ if (a.length !== b.length || !timingSafeEqual(a, b)) {
55
+ return { ok: false, reason: 'bad_signature' };
56
+ }
57
+ const expiresAt = Number(payload.readBigUInt64BE(PAYLOAD_RANDOM_BYTES));
58
+ if (Math.floor(nowMs / 1000) >= expiresAt) {
59
+ return { ok: false, reason: 'expired' };
60
+ }
61
+ return { ok: true, expiresAt };
62
+ }
63
+ /**
64
+ * Validate a client_credentials grant request against the configured MCP env
65
+ * + the read secret. Returns the access token on success.
66
+ */
67
+ export function grantClientCredentials(req, env, readSecret, nowMs = Date.now()) {
68
+ if (req.grantType !== 'client_credentials') {
69
+ return {
70
+ ok: false,
71
+ status: 400,
72
+ error: 'unsupported_grant_type',
73
+ description: 'only client_credentials is supported',
74
+ };
75
+ }
76
+ if (!req.clientId || !req.clientSecret) {
77
+ return { ok: false, status: 400, error: 'invalid_request', description: 'missing credentials' };
78
+ }
79
+ const cidA = Buffer.from(req.clientId);
80
+ const cidB = Buffer.from(env.clientId);
81
+ const secA = Buffer.from(req.clientSecret);
82
+ const secB = Buffer.from(readSecret);
83
+ const cidOk = cidA.length === cidB.length && timingSafeEqual(cidA, cidB);
84
+ const secOk = secA.length === secB.length && timingSafeEqual(secA, secB);
85
+ if (!cidOk || !secOk) {
86
+ return { ok: false, status: 401, error: 'invalid_client' };
87
+ }
88
+ return { ok: true, token: issueToken(env, nowMs) };
89
+ }
90
+ //# sourceMappingURL=oauth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauth.js","sourceRoot":"","sources":["../../src/mcp/oauth.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,35 @@
1
+ import type { TSchema } from '@sinclair/typebox';
2
+ export interface ToolDefinition {
3
+ name: string;
4
+ description: string;
5
+ inputSchema: TSchema;
6
+ handler: (args: any) => Promise<unknown> | unknown;
7
+ }
8
+ export interface JsonRpcRequest {
9
+ jsonrpc: '2.0';
10
+ id?: string | number | null;
11
+ method: string;
12
+ params?: unknown;
13
+ }
14
+ export interface JsonRpcSuccess {
15
+ jsonrpc: '2.0';
16
+ id: string | number | null;
17
+ result: unknown;
18
+ }
19
+ export interface JsonRpcError {
20
+ jsonrpc: '2.0';
21
+ id: string | number | null;
22
+ error: {
23
+ code: number;
24
+ message: string;
25
+ data?: unknown;
26
+ };
27
+ }
28
+ export type JsonRpcResponse = JsonRpcSuccess | JsonRpcError;
29
+ export declare const MCP_PROTOCOL_VERSION = "2025-06-18";
30
+ export declare const MCP_SERVER_NAME = "zyke-sync";
31
+ export interface McpServer {
32
+ handle(req: unknown): Promise<JsonRpcResponse>;
33
+ listToolNames(): string[];
34
+ }
35
+ export declare function createMcpServer(tools: ToolDefinition[]): McpServer;
@@ -0,0 +1,94 @@
1
+ import { Value } from '@sinclair/typebox/value';
2
+ export const MCP_PROTOCOL_VERSION = '2025-06-18';
3
+ export const MCP_SERVER_NAME = 'zyke-sync';
4
+ const ERR_PARSE = -32700;
5
+ const ERR_INVALID_REQUEST = -32600;
6
+ const ERR_METHOD_NOT_FOUND = -32601;
7
+ const ERR_INVALID_PARAMS = -32602;
8
+ const ERR_INTERNAL = -32603;
9
+ function errorResponse(id, code, message, data) {
10
+ const err = { code, message };
11
+ if (data !== undefined)
12
+ err.data = data;
13
+ return { jsonrpc: '2.0', id, error: err };
14
+ }
15
+ function successResponse(id, result) {
16
+ return { jsonrpc: '2.0', id, result };
17
+ }
18
+ export function createMcpServer(tools) {
19
+ const toolMap = new Map(tools.map((t) => [t.name, t]));
20
+ async function dispatch(req) {
21
+ const id = req.id ?? null;
22
+ switch (req.method) {
23
+ case 'initialize':
24
+ return successResponse(id, {
25
+ protocolVersion: MCP_PROTOCOL_VERSION,
26
+ capabilities: { tools: { listChanged: false } },
27
+ serverInfo: { name: MCP_SERVER_NAME, version: '1.0.0' },
28
+ });
29
+ case 'notifications/initialized':
30
+ case 'ping':
31
+ return successResponse(id, {});
32
+ case 'tools/list':
33
+ return successResponse(id, {
34
+ tools: tools.map((t) => ({
35
+ name: t.name,
36
+ description: t.description,
37
+ inputSchema: t.inputSchema,
38
+ })),
39
+ });
40
+ case 'tools/call': {
41
+ const params = req.params;
42
+ if (!params || typeof params.name !== 'string') {
43
+ return errorResponse(id, ERR_INVALID_PARAMS, 'missing tool name');
44
+ }
45
+ const tool = toolMap.get(params.name);
46
+ if (!tool)
47
+ return errorResponse(id, ERR_METHOD_NOT_FOUND, `unknown tool: ${params.name}`);
48
+ const args = params.arguments ?? {};
49
+ if (!Value.Check(tool.inputSchema, args)) {
50
+ const first = [...Value.Errors(tool.inputSchema, args)][0];
51
+ const detail = first ? `${first.path} ${first.message}`.trim() : 'invalid arguments';
52
+ return errorResponse(id, ERR_INVALID_PARAMS, detail);
53
+ }
54
+ try {
55
+ const result = await tool.handler(args);
56
+ return successResponse(id, {
57
+ content: [{ type: 'text', text: JSON.stringify(result) }],
58
+ structuredContent: result,
59
+ isError: false,
60
+ });
61
+ }
62
+ catch (err) {
63
+ const message = err instanceof Error ? err.message : String(err);
64
+ return successResponse(id, {
65
+ content: [{ type: 'text', text: `tool error: ${message}` }],
66
+ isError: true,
67
+ });
68
+ }
69
+ }
70
+ default:
71
+ return errorResponse(id, ERR_METHOD_NOT_FOUND, `unknown method: ${req.method}`);
72
+ }
73
+ }
74
+ return {
75
+ listToolNames: () => tools.map((t) => t.name),
76
+ async handle(raw) {
77
+ if (!raw || typeof raw !== 'object') {
78
+ return errorResponse(null, ERR_PARSE, 'request must be a JSON object');
79
+ }
80
+ const req = raw;
81
+ if (req.jsonrpc !== '2.0' || typeof req.method !== 'string') {
82
+ return errorResponse(req.id ?? null, ERR_INVALID_REQUEST, 'malformed JSON-RPC request');
83
+ }
84
+ try {
85
+ return await dispatch(req);
86
+ }
87
+ catch (err) {
88
+ const message = err instanceof Error ? err.message : String(err);
89
+ return errorResponse(req.id ?? null, ERR_INTERNAL, message);
90
+ }
91
+ },
92
+ };
93
+ }
94
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/mcp/server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ import type { Db } from '../db/client.js';
2
+ export interface TimezoneRow {
3
+ timezone: string;
4
+ utcOffsetMinutes: number;
5
+ effectiveFrom: number;
6
+ }
7
+ export interface TimezoneResolver {
8
+ /** Returns the timezone active at the given UTC ms, or null if none. */
9
+ resolveAt(utcMs: number): TimezoneRow | null;
10
+ /** Force-reload from DB. Called between requests if needed. */
11
+ reload(): Promise<void>;
12
+ }
13
+ /**
14
+ * Cache the (small) user_timezones table in memory, sorted by effectiveFrom
15
+ * descending. Lookups are O(log n) via binary search on the sorted list.
16
+ */
17
+ export declare function createTimezoneResolver(db: Db): TimezoneResolver;
@@ -0,0 +1,32 @@
1
+ import { desc } from 'drizzle-orm';
2
+ import { userTimezones } from '../db/schema.js';
3
+ /**
4
+ * Cache the (small) user_timezones table in memory, sorted by effectiveFrom
5
+ * descending. Lookups are O(log n) via binary search on the sorted list.
6
+ */
7
+ export function createTimezoneResolver(db) {
8
+ let cache = [];
9
+ async function reload() {
10
+ const rows = await db
11
+ .select({
12
+ timezone: userTimezones.timezone,
13
+ utcOffsetMinutes: userTimezones.utcOffsetMinutes,
14
+ effectiveFrom: userTimezones.effectiveFrom,
15
+ })
16
+ .from(userTimezones)
17
+ .orderBy(desc(userTimezones.effectiveFrom));
18
+ cache = rows;
19
+ }
20
+ function resolveAt(utcMs) {
21
+ if (!Number.isFinite(utcMs))
22
+ return null;
23
+ // cache is sorted descending by effectiveFrom; first row whose effectiveFrom <= utcMs wins.
24
+ for (const row of cache) {
25
+ if (row.effectiveFrom <= utcMs)
26
+ return row;
27
+ }
28
+ return null;
29
+ }
30
+ return { resolveAt, reload };
31
+ }
32
+ //# sourceMappingURL=timezones.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"timezones.js","sourceRoot":"","sources":["../../src/mcp/timezones.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AAGnC,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAehD;;;GAGG;AACH,MAAM,UAAU,sBAAsB,CAAC,EAAM;IAC3C,IAAI,KAAK,GAAkB,EAAE,CAAC;IAE9B,KAAK,UAAU,MAAM;QACnB,MAAM,IAAI,GAAG,MAAM,EAAE;aAClB,MAAM,CAAC;YACN,QAAQ,EAAE,aAAa,CAAC,QAAQ;YAChC,gBAAgB,EAAE,aAAa,CAAC,gBAAgB;YAChD,aAAa,EAAE,aAAa,CAAC,aAAa;SAC3C,CAAC;aACD,IAAI,CAAC,aAAa,CAAC;aACnB,OAAO,CAAC,IAAI,CAAC,aAAa,CAAC,aAAa,CAAC,CAAC,CAAC;QAC9C,KAAK,GAAG,IAAI,CAAC;IACf,CAAC;IAED,SAAS,SAAS,CAAC,KAAa;QAC9B,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC;YAAE,OAAO,IAAI,CAAC;QACzC,4FAA4F;QAC5F,KAAK,MAAM,GAAG,IAAI,KAAK,EAAE,CAAC;YACxB,IAAI,GAAG,CAAC,aAAa,IAAI,KAAK;gBAAE,OAAO,GAAG,CAAC;QAC7C,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC;AAC/B,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { Db } from '../db/client.js';
2
+ import type { ToolDefinition } from './server.js';
3
+ import type { TimezoneResolver } from './timezones.js';
4
+ export declare function buildTools(db: Db, tz: TimezoneResolver): ToolDefinition[];
@@ -0,0 +1,236 @@
1
+ import { Type } from '@sinclair/typebox';
2
+ import { and, asc, desc, eq, gt, gte, lte } from 'drizzle-orm';
3
+ import { dailyMetrics, dailyStressBurden, heartRateMinute, hrZoneHistory, sleepSessions, userProfile, userTimezones, weeklyMetrics, } from '../db/schema.js';
4
+ import { inferValueType } from './metric-types.js';
5
+ import { formatHmsFromSeconds, formatTzIso } from './transforms.js';
6
+ const Limit = Type.Optional(Type.Integer({ minimum: 1, maximum: 500, default: 100 }));
7
+ const IsoDate = Type.String({ pattern: '^\\d{4}-\\d{2}-\\d{2}$' });
8
+ const UnixMs = Type.Integer({ minimum: 0 });
9
+ function tzIso(resolver, utcMs) {
10
+ const row = resolver.resolveAt(utcMs);
11
+ if (!row)
12
+ return null;
13
+ return formatTzIso(utcMs, row.utcOffsetMinutes);
14
+ }
15
+ function applyLimit(n) {
16
+ if (n === undefined)
17
+ return 100;
18
+ return Math.min(Math.max(1, n), 500);
19
+ }
20
+ function and$(...parts) {
21
+ const filtered = parts.filter((p) => p !== undefined);
22
+ if (filtered.length === 0)
23
+ return undefined;
24
+ if (filtered.length === 1)
25
+ return filtered[0];
26
+ return and(...filtered);
27
+ }
28
+ export function buildTools(db, tz) {
29
+ return [
30
+ {
31
+ name: 'list_user_timezones',
32
+ description: 'List user timezone history entries (initial / travel / DST). Each row records the timezone that became active at `effectiveFrom` (UTC ms).',
33
+ inputSchema: Type.Object({
34
+ since: Type.Optional(UnixMs),
35
+ until: Type.Optional(UnixMs),
36
+ limit: Limit,
37
+ }, { additionalProperties: false }),
38
+ async handler(args) {
39
+ await tz.reload();
40
+ const where = and$(args.since !== undefined ? gte(userTimezones.effectiveFrom, args.since) : undefined, args.until !== undefined ? lte(userTimezones.effectiveFrom, args.until) : undefined);
41
+ const q = db.select().from(userTimezones).limit(applyLimit(args.limit));
42
+ const rows = where
43
+ ? await q.where(where).orderBy(desc(userTimezones.effectiveFrom))
44
+ : await q.orderBy(desc(userTimezones.effectiveFrom));
45
+ return { rows };
46
+ },
47
+ },
48
+ {
49
+ name: 'get_user_profile',
50
+ description: 'Return the singleton user profile row, or null if none has been synced.',
51
+ inputSchema: Type.Object({}, { additionalProperties: false }),
52
+ async handler() {
53
+ const rows = await db.select().from(userProfile).where(eq(userProfile.id, 1)).limit(1);
54
+ return { profile: rows[0] ?? null };
55
+ },
56
+ },
57
+ {
58
+ name: 'list_daily_metrics',
59
+ description: 'List daily metrics with a `valueType` annotation inferred from the metric key (e.g. sleep_optimum_s → seconds).',
60
+ inputSchema: Type.Object({
61
+ isoDateFrom: Type.Optional(IsoDate),
62
+ isoDateTo: Type.Optional(IsoDate),
63
+ metricKey: Type.Optional(Type.String({ minLength: 1, maxLength: 64 })),
64
+ limit: Limit,
65
+ }, { additionalProperties: false }),
66
+ async handler(args) {
67
+ const where = and$(args.isoDateFrom ? gte(dailyMetrics.isoDate, args.isoDateFrom) : undefined, args.isoDateTo ? lte(dailyMetrics.isoDate, args.isoDateTo) : undefined, args.metricKey ? eq(dailyMetrics.metricKey, args.metricKey) : undefined);
68
+ const q = db
69
+ .select()
70
+ .from(dailyMetrics)
71
+ .orderBy(desc(dailyMetrics.isoDate), asc(dailyMetrics.metricKey))
72
+ .limit(applyLimit(args.limit));
73
+ const rows = where ? await q.where(where) : await q;
74
+ return {
75
+ rows: rows.map((r) => ({ ...r, valueType: inferValueType(r.metricKey) })),
76
+ };
77
+ },
78
+ },
79
+ {
80
+ name: 'list_weekly_metrics',
81
+ description: 'List weekly metrics with `valueType` annotations.',
82
+ inputSchema: Type.Object({
83
+ weekStartFrom: Type.Optional(IsoDate),
84
+ weekStartTo: Type.Optional(IsoDate),
85
+ metricKey: Type.Optional(Type.String({ minLength: 1, maxLength: 64 })),
86
+ limit: Limit,
87
+ }, { additionalProperties: false }),
88
+ async handler(args) {
89
+ const where = and$(args.weekStartFrom ? gte(weeklyMetrics.weekStartIsoDate, args.weekStartFrom) : undefined, args.weekStartTo ? lte(weeklyMetrics.weekStartIsoDate, args.weekStartTo) : undefined, args.metricKey ? eq(weeklyMetrics.metricKey, args.metricKey) : undefined);
90
+ const q = db
91
+ .select()
92
+ .from(weeklyMetrics)
93
+ .orderBy(desc(weeklyMetrics.weekStartIsoDate), asc(weeklyMetrics.metricKey))
94
+ .limit(applyLimit(args.limit));
95
+ const rows = where ? await q.where(where) : await q;
96
+ return {
97
+ rows: rows.map((r) => ({ ...r, valueType: inferValueType(r.metricKey) })),
98
+ };
99
+ },
100
+ },
101
+ {
102
+ name: 'list_daily_stress_burden',
103
+ description: 'List daily stress burden summaries. `peakScoreTz` is the offset-aware ISO string of `peakScoreUtc`.',
104
+ inputSchema: Type.Object({
105
+ isoDateFrom: Type.Optional(IsoDate),
106
+ isoDateTo: Type.Optional(IsoDate),
107
+ limit: Limit,
108
+ }, { additionalProperties: false }),
109
+ async handler(args) {
110
+ await tz.reload();
111
+ const where = and$(args.isoDateFrom ? gte(dailyStressBurden.isoDate, args.isoDateFrom) : undefined, args.isoDateTo ? lte(dailyStressBurden.isoDate, args.isoDateTo) : undefined);
112
+ const q = db
113
+ .select()
114
+ .from(dailyStressBurden)
115
+ .orderBy(desc(dailyStressBurden.isoDate))
116
+ .limit(applyLimit(args.limit));
117
+ const rows = where ? await q.where(where) : await q;
118
+ return {
119
+ rows: rows.map((r) => ({ ...r, peakScoreTz: tzIso(tz, r.peakScoreUtc) })),
120
+ };
121
+ },
122
+ },
123
+ {
124
+ name: 'list_heart_rate_minute',
125
+ description: 'List minute-aggregated heart rate rows. `minuteStartTz` is the offset-aware ISO string of `minuteStartUtc`. Use cursor pagination for large windows.',
126
+ inputSchema: Type.Object({
127
+ fromUtcMs: Type.Optional(UnixMs),
128
+ toUtcMs: Type.Optional(UnixMs),
129
+ deviceId: Type.Optional(Type.Integer()),
130
+ cursor: Type.Optional(UnixMs),
131
+ limit: Limit,
132
+ }, { additionalProperties: false }),
133
+ async handler(args) {
134
+ await tz.reload();
135
+ const lim = applyLimit(args.limit);
136
+ const where = and$(args.fromUtcMs !== undefined
137
+ ? gte(heartRateMinute.minuteStartUtc, args.fromUtcMs)
138
+ : undefined, args.toUtcMs !== undefined
139
+ ? lte(heartRateMinute.minuteStartUtc, args.toUtcMs)
140
+ : undefined, args.deviceId !== undefined ? eq(heartRateMinute.deviceId, args.deviceId) : undefined, args.cursor !== undefined ? gt(heartRateMinute.minuteStartUtc, args.cursor) : undefined);
141
+ const q = db
142
+ .select()
143
+ .from(heartRateMinute)
144
+ .orderBy(asc(heartRateMinute.minuteStartUtc))
145
+ .limit(lim);
146
+ const rows = where ? await q.where(where) : await q;
147
+ const decorated = rows.map((r) => ({ ...r, minuteStartTz: tzIso(tz, r.minuteStartUtc) }));
148
+ const last = rows[rows.length - 1];
149
+ const nextCursor = rows.length === lim && last ? last.minuteStartUtc : null;
150
+ return { rows: decorated, nextCursor };
151
+ },
152
+ },
153
+ {
154
+ name: 'list_sleep_sessions',
155
+ description: 'List sleep sessions with timezone-aware timestamps (sleepOnsetTz, wakeTimeTz, cycle startTz/endTz) and human-readable durations (wasoR, restorativeSleepR, sleepDeficitR as hh:mm:ss).',
156
+ inputSchema: Type.Object({
157
+ isoDateFrom: Type.Optional(IsoDate),
158
+ isoDateTo: Type.Optional(IsoDate),
159
+ limit: Limit,
160
+ }, { additionalProperties: false }),
161
+ async handler(args) {
162
+ await tz.reload();
163
+ const where = and$(args.isoDateFrom ? gte(sleepSessions.isoDate, args.isoDateFrom) : undefined, args.isoDateTo ? lte(sleepSessions.isoDate, args.isoDateTo) : undefined);
164
+ const q = db
165
+ .select()
166
+ .from(sleepSessions)
167
+ .orderBy(desc(sleepSessions.isoDate))
168
+ .limit(applyLimit(args.limit));
169
+ const rows = where ? await q.where(where) : await q;
170
+ return { rows: rows.map((r) => decorateSleepSession(r, tz)) };
171
+ },
172
+ },
173
+ {
174
+ name: 'list_hr_zone_history',
175
+ description: 'List HR zone boundary history. Each row records the bpm lower bounds for zones 1..5 effective from `effectiveFromIsoDate`.',
176
+ inputSchema: Type.Object({
177
+ effectiveFromDate: Type.Optional(IsoDate),
178
+ limit: Limit,
179
+ }, { additionalProperties: false }),
180
+ async handler(args) {
181
+ const where = args.effectiveFromDate
182
+ ? gte(hrZoneHistory.effectiveFromIsoDate, args.effectiveFromDate)
183
+ : undefined;
184
+ const q = db
185
+ .select()
186
+ .from(hrZoneHistory)
187
+ .orderBy(desc(hrZoneHistory.effectiveFromIsoDate))
188
+ .limit(applyLimit(args.limit));
189
+ const rows = where ? await q.where(where) : await q;
190
+ return { rows };
191
+ },
192
+ },
193
+ ];
194
+ }
195
+ /**
196
+ * Decorate a sleep session row with timezone-aware timestamps and
197
+ * human-readable duration strings. The mobile schema deliberately stores the
198
+ * domain shape as opaque JSON, so we narrow defensively per field.
199
+ */
200
+ function decorateSleepSession(row, tz) {
201
+ const payload = row.payload ?? {};
202
+ const decoratedPayload = { ...payload };
203
+ const sleepOnsetS = numberOrNull(payload.sleepOnsetS);
204
+ const wakeTimeS = numberOrNull(payload.wakeTimeS);
205
+ const wasoS = numberOrNull(payload.wasoS);
206
+ const restorativeSleepS = numberOrNull(payload.restorativeSleepS);
207
+ const sleepDeficitS = numberOrNull(payload.sleepDeficitS);
208
+ if (sleepOnsetS !== null)
209
+ decoratedPayload.sleepOnsetTz = tzIso(tz, sleepOnsetS * 1000);
210
+ if (wakeTimeS !== null)
211
+ decoratedPayload.wakeTimeTz = tzIso(tz, wakeTimeS * 1000);
212
+ if (wasoS !== null)
213
+ decoratedPayload.wasoR = formatHmsFromSeconds(wasoS);
214
+ if (restorativeSleepS !== null)
215
+ decoratedPayload.restorativeSleepR = formatHmsFromSeconds(restorativeSleepS);
216
+ if (sleepDeficitS !== null)
217
+ decoratedPayload.sleepDeficitR = formatHmsFromSeconds(sleepDeficitS);
218
+ const cycles = Array.isArray(payload.cycles) ? payload.cycles : [];
219
+ decoratedPayload.cycles = cycles.map((c) => {
220
+ if (!c || typeof c !== 'object')
221
+ return c;
222
+ const cycle = c;
223
+ const startS = numberOrNull(cycle.startS);
224
+ const endS = numberOrNull(cycle.endS);
225
+ return {
226
+ ...cycle,
227
+ ...(startS !== null ? { startTz: tzIso(tz, startS * 1000) } : {}),
228
+ ...(endS !== null ? { endTz: tzIso(tz, endS * 1000) } : {}),
229
+ };
230
+ });
231
+ return { ...row, payload: decoratedPayload };
232
+ }
233
+ function numberOrNull(v) {
234
+ return typeof v === 'number' && Number.isFinite(v) ? v : null;
235
+ }
236
+ //# sourceMappingURL=tools.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tools.js","sourceRoot":"","sources":["../../src/mcp/tools.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Format a UTC instant as an ISO 8601 string with a timezone offset attached.
3
+ *
4
+ * formatTzIso(1716115262000, 120) === '2024-05-19T12:01:02+02:00'
5
+ *
6
+ * Seconds precision only — sub-second is intentionally dropped because none of
7
+ * the source timestamps carry it meaningfully.
8
+ */
9
+ export declare function formatTzIso(utcMs: number, offsetMinutes: number): string;
10
+ /**
11
+ * Format a signed duration in seconds as `hh:mm:ss` (or `-hh:mm:ss`). Supports
12
+ * durations larger than 24h — hours are not modulo'd.
13
+ */
14
+ export declare function formatHmsFromSeconds(totalSeconds: number): string;
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Format a UTC instant as an ISO 8601 string with a timezone offset attached.
3
+ *
4
+ * formatTzIso(1716115262000, 120) === '2024-05-19T12:01:02+02:00'
5
+ *
6
+ * Seconds precision only — sub-second is intentionally dropped because none of
7
+ * the source timestamps carry it meaningfully.
8
+ */
9
+ export function formatTzIso(utcMs, offsetMinutes) {
10
+ if (!Number.isFinite(utcMs) || !Number.isFinite(offsetMinutes)) {
11
+ throw new Error(`formatTzIso: non-finite input (${utcMs}, ${offsetMinutes})`);
12
+ }
13
+ const shifted = new Date(utcMs + offsetMinutes * 60_000);
14
+ const yyyy = shifted.getUTCFullYear().toString().padStart(4, '0');
15
+ const mm = (shifted.getUTCMonth() + 1).toString().padStart(2, '0');
16
+ const dd = shifted.getUTCDate().toString().padStart(2, '0');
17
+ const HH = shifted.getUTCHours().toString().padStart(2, '0');
18
+ const MM = shifted.getUTCMinutes().toString().padStart(2, '0');
19
+ const SS = shifted.getUTCSeconds().toString().padStart(2, '0');
20
+ const sign = offsetMinutes >= 0 ? '+' : '-';
21
+ const abs = Math.abs(offsetMinutes);
22
+ const offH = Math.floor(abs / 60)
23
+ .toString()
24
+ .padStart(2, '0');
25
+ const offM = (abs % 60).toString().padStart(2, '0');
26
+ return `${yyyy}-${mm}-${dd}T${HH}:${MM}:${SS}${sign}${offH}:${offM}`;
27
+ }
28
+ /**
29
+ * Format a signed duration in seconds as `hh:mm:ss` (or `-hh:mm:ss`). Supports
30
+ * durations larger than 24h — hours are not modulo'd.
31
+ */
32
+ export function formatHmsFromSeconds(totalSeconds) {
33
+ if (!Number.isFinite(totalSeconds)) {
34
+ throw new Error(`formatHmsFromSeconds: non-finite input (${totalSeconds})`);
35
+ }
36
+ const sign = totalSeconds < 0 ? '-' : '';
37
+ const abs = Math.floor(Math.abs(totalSeconds));
38
+ const h = Math.floor(abs / 3600);
39
+ const m = Math.floor((abs % 3600) / 60);
40
+ const s = abs % 60;
41
+ return `${sign}${h.toString().padStart(2, '0')}:${m.toString().padStart(2, '0')}:${s
42
+ .toString()
43
+ .padStart(2, '0')}`;
44
+ }
45
+ //# sourceMappingURL=transforms.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transforms.js","sourceRoot":"","sources":["../../src/mcp/transforms.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import { Hono } from 'hono';
2
+ import type { Env, McpEnv } from '../env.js';
3
+ import type { Db } from '../db/client.js';
4
+ export interface McpDeps {
5
+ db: Db;
6
+ env: Env;
7
+ mcp: McpEnv;
8
+ }
9
+ export declare function mcpRoutes(deps: McpDeps): Hono;
@@ -0,0 +1,96 @@
1
+ import { Hono } from 'hono';
2
+ import { grantClientCredentials, verifyToken } from '../mcp/oauth.js';
3
+ import { createMcpServer } from '../mcp/server.js';
4
+ import { buildTools } from '../mcp/tools.js';
5
+ import { createTimezoneResolver } from '../mcp/timezones.js';
6
+ export function mcpRoutes(deps) {
7
+ const { db, env, mcp } = deps;
8
+ const tz = createTimezoneResolver(db);
9
+ const server = createMcpServer(buildTools(db, tz));
10
+ const app = new Hono();
11
+ // OAuth: client_credentials grant.
12
+ app.post('/oauth/token', async (c) => {
13
+ let params;
14
+ const ct = c.req.header('content-type') ?? '';
15
+ try {
16
+ if (ct.includes('application/x-www-form-urlencoded')) {
17
+ params = new URLSearchParams(await c.req.text());
18
+ }
19
+ else if (ct.includes('application/json')) {
20
+ const body = (await c.req.json());
21
+ params = new URLSearchParams();
22
+ for (const [k, v] of Object.entries(body)) {
23
+ if (typeof v === 'string')
24
+ params.set(k, v);
25
+ }
26
+ }
27
+ else {
28
+ return c.json({ error: 'invalid_request', error_description: 'unsupported content-type' }, 400);
29
+ }
30
+ }
31
+ catch {
32
+ return c.json({ error: 'invalid_request', error_description: 'malformed body' }, 400);
33
+ }
34
+ // RFC 6749 §2.3.1: client credentials may also be supplied via Basic auth.
35
+ let clientId = params.get('client_id') ?? '';
36
+ let clientSecret = params.get('client_secret') ?? '';
37
+ const basic = c.req.header('authorization');
38
+ if (basic?.toLowerCase().startsWith('basic ')) {
39
+ const decoded = Buffer.from(basic.slice(6), 'base64').toString('utf8');
40
+ const sep = decoded.indexOf(':');
41
+ if (sep > 0) {
42
+ if (!clientId)
43
+ clientId = decoded.slice(0, sep);
44
+ if (!clientSecret)
45
+ clientSecret = decoded.slice(sep + 1);
46
+ }
47
+ }
48
+ const result = grantClientCredentials({ clientId, clientSecret, grantType: params.get('grant_type') ?? '' }, mcp, env.readSecret);
49
+ if (!result.ok) {
50
+ return c.json({
51
+ error: result.error,
52
+ ...(result.description ? { error_description: result.description } : {}),
53
+ }, result.status);
54
+ }
55
+ return c.json({
56
+ access_token: result.token.accessToken,
57
+ token_type: 'Bearer',
58
+ expires_in: result.token.expiresIn,
59
+ });
60
+ });
61
+ // OAuth metadata discovery (RFC 8414).
62
+ app.get('/oauth/.well-known/oauth-authorization-server', (c) => {
63
+ const base = new URL(c.req.url);
64
+ const issuer = `${base.protocol}//${base.host}`;
65
+ return c.json({
66
+ issuer,
67
+ token_endpoint: `${issuer}/mcp/oauth/token`,
68
+ grant_types_supported: ['client_credentials'],
69
+ token_endpoint_auth_methods_supported: ['client_secret_post', 'client_secret_basic'],
70
+ response_types_supported: [],
71
+ });
72
+ });
73
+ // MCP endpoint (JSON-RPC over POST). Requires Bearer token.
74
+ app.post('/', async (c) => {
75
+ const auth = c.req.header('authorization') ?? '';
76
+ if (!auth.toLowerCase().startsWith('bearer ')) {
77
+ return c.json({ error: 'unauthorized' }, 401);
78
+ }
79
+ const token = auth.slice(7).trim();
80
+ const v = verifyToken(token, mcp);
81
+ if (!v.ok) {
82
+ return c.json({ error: 'unauthorized', reason: v.reason }, 401);
83
+ }
84
+ let body;
85
+ try {
86
+ body = await c.req.json();
87
+ }
88
+ catch {
89
+ return c.json({ jsonrpc: '2.0', id: null, error: { code: -32700, message: 'parse error' } }, 400);
90
+ }
91
+ const response = await server.handle(body);
92
+ return c.json(response);
93
+ });
94
+ return app;
95
+ }
96
+ //# sourceMappingURL=mcp.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp.js","sourceRoot":"","sources":["../../src/routes/mcp.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zykeco/sync-server",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "description": "Self-hosted Zyke sync server (Hono + Drizzle + libsql). Imports to auto-start; ships a migrate CLI.",
5
5
  "license": "AGPL-3.0-or-later",
6
6
  "author": "P2LS9K GmbH <office@p2ls9k.com>",
@@ -43,7 +43,7 @@
43
43
  "scripts": {
44
44
  "build": "tsc -p tsconfig.json && chmod +x dist/migrate-cli.js dist/startup-cli.js dist/upgrade-cli.js",
45
45
  "typecheck": "tsc -p tsconfig.json --noEmit",
46
- "test": "node --test",
46
+ "test": "node --import tsx --test 'test/**/*.test.ts'",
47
47
  "dev": "tsx --watch src/index.ts",
48
48
  "start": "node dist/index.js",
49
49
  "clean": "rm -rf dist .tsbuildinfo",