learning_ai_common_plat/services/mcp-server/src/lib/nomgap-client.ts
saravanakumardb1 3f296a8e72 feat(mcp-server): fill 12 DOMAIN_PRODUCTS.md MCP tool gaps + client fn additions
Bug fix (committed separately):
  social-fast-coordinator: stage_transition used non-existent currentStage

New client functions:
  nomgap-client: nomgapFastingCreateSession, nomgapProtocolGet,
                 nomgapSocialListGroupFasts (+ GroupFastDoc)
  peakpulse-client: peakpulseRoutesList, peakpulseSyncStatus
  lysnrai-client: lysnraiApiTokenCreate, lysnraiSessionsStats,
                  lysnraiTranscriptsExportBatch
  chronomind-client: chronomindRoutineGet, chronomindSharedTimerShare

New MCP tools (12):
  mindlyst.briefs.generate        — trigger daily brief via mindlystBriefCreate
  mindlyst.memory.getTriageResult — extract TriageResult sub-doc only
  nomgap.fasting.createSession    — start new fast with protocolId
  nomgap.protocols.get            — single protocol lookup
  nomgap.social.listGroupFasts    — list group fast sessions
  peakpulse.routes.list           — list GPS r
Bug fix (committed separately):
  social-fast-coordinator: stage_transition used non-existent currentStage
ats  social-fast-coordinator: staon
New client functions:
  nomgap-client: nomgapFastingCreateSession, nomgarai  nomgaens.rotate                      nomgapSocialListGroupFasts (+ GroupFastDoc)
 d.  peakpulse-client: peakpulseRoutesList, peakpulseSyncStaturg  lysnrai-client: lysnraiApiTokenCreate, lysnraiSessionsStati                  lysnraiTranscriptsExpor                    us  chronomind-client: chronomindRoutineGet, chrerver total: 126 tools across 17 namespaces
2026-03-05 18:19:04 -08:00

227 lines
7.5 KiB
TypeScript

/**
* NomGap backend client — typed HTTP wrappers for the nomgap-backend (port 4013).
*
* Auth: Bearer token from the caller's JWT (same JWT_SECRET as platform-service).
*/
import { config } from './config.js';
export interface NomGapClientOptions {
token?: string;
requestId?: string;
productId?: string;
}
async function nomgapFetch<T>(
path: string,
init: RequestInit,
opts: NomGapClientOptions
): Promise<T> {
const headers: Record<string, string> = {
'Content-Type': 'application/json',
...(opts.token ? { Authorization: `Bearer ${opts.token}` } : {}),
...(opts.requestId ? { 'x-request-id': opts.requestId } : {}),
...(opts.productId ? { 'x-product-id': opts.productId } : { 'x-product-id': 'nomgap' }),
};
const res = await fetch(`${config.NOMGAP_BACKEND_URL}/api${path}`, {
...init,
headers: { ...((init.headers as Record<string, string>) ?? {}), ...headers },
signal: AbortSignal.timeout(15_000),
});
if (!res.ok) {
const body = await res.text().catch(() => '');
throw new Error(`nomgap-backend ${init.method ?? 'GET'} ${path}${res.status}: ${body}`);
}
return res.json() as Promise<T>;
}
// ── Fasting sessions ───────────────────────────────────────────────────────
export interface FastingSessionDoc {
id: string;
userId: string;
productId: string;
protocolId: string;
startedAt: number;
targetDurationMs: number;
status: 'active' | 'completed' | 'broken';
stages: unknown[];
moodCheckins: unknown[];
waterIntake: unknown[];
metrics: {
actualDurationMs: number;
completionRatio: number;
peakAutophagyConfidence: number;
totalPausedMs: number;
moodCheckinCount: number;
averageEnergy: number | null;
averageMood: number | null;
};
createdAt: string;
updatedAt: string;
}
export function nomgapFastingCreateSession(
input: { protocolId: string; variables?: Record<string, string> },
opts: NomGapClientOptions
): Promise<FastingSessionDoc> {
return nomgapFetch('/fasting/sessions', { method: 'POST', body: JSON.stringify(input) }, opts);
}
export function nomgapFastingSessionsList(
params: { limit?: number; offset?: number; status?: string; from?: string; to?: string },
opts: NomGapClientOptions
): Promise<{ items: FastingSessionDoc[]; total: number }> {
const qs = new URLSearchParams();
if (params.limit !== undefined) qs.set('limit', String(params.limit));
if (params.offset !== undefined) qs.set('offset', String(params.offset));
if (params.status) qs.set('status', params.status);
if (params.from) qs.set('from', params.from);
if (params.to) qs.set('to', params.to);
const q = qs.toString();
return nomgapFetch(`/fasting/sessions${q ? `?${q}` : ''}`, { method: 'GET' }, opts);
}
export function nomgapFastingSessionGet(
sessionId: string,
opts: NomGapClientOptions
): Promise<FastingSessionDoc> {
return nomgapFetch(`/fasting/sessions/${sessionId}`, { method: 'GET' }, opts);
}
export function nomgapFastingGetStats(opts: NomGapClientOptions): Promise<Record<string, unknown>> {
return nomgapFetch('/fasting/stats', { method: 'GET' }, opts);
}
export function nomgapFastingGetWeeklyStats(
opts: NomGapClientOptions
): Promise<Record<string, unknown>> {
return nomgapFetch('/fasting/stats/weekly', { method: 'GET' }, opts);
}
// ── Protocols ─────────────────────────────────────────────────────────────
export function nomgapProtocolsList(
opts: NomGapClientOptions
): Promise<{ protocols: unknown[]; total: number }> {
return nomgapFetch('/fasting/protocols', { method: 'GET' }, opts);
}
export function nomgapProtocolGet(
protocolId: string,
opts: NomGapClientOptions
): Promise<Record<string, unknown>> {
return nomgapFetch(`/fasting/protocols/${protocolId}`, { method: 'GET' }, opts);
}
// ── Body stages (public — no auth required) ────────────────────────────────
export function nomgapBodyStagesList(
opts: Pick<NomGapClientOptions, 'requestId'>
): Promise<{ stages: unknown[]; total: number }> {
return nomgapFetch('/fasting/stages', { method: 'GET' }, opts);
}
// ── Autophagy confidence ──────────────────────────────────────────────────
export interface AutophagyConfidenceInput {
durationHours: number;
activityLevel: 'sedentary' | 'light' | 'moderate' | 'active' | 'very_active';
lastMealCarbs?: number;
sleepHours?: number;
completionHistory?: { totalFasts: number; completionRate: number };
hrvData?: { restingHR?: number; hrv?: number };
}
export interface AutophagyConfidenceResult {
confidence: number;
label: 'unlikely' | 'possible' | 'likely' | 'very_likely' | 'near_certain';
breakdown: {
duration: number;
meal: number;
activity: number;
sleep: number;
history: number;
hrv: number;
};
currentStage: string;
}
export function nomgapAutophagyConfidence(
input: AutophagyConfidenceInput,
opts: Pick<NomGapClientOptions, 'requestId'>
): Promise<AutophagyConfidenceResult> {
return nomgapFetch(
'/fasting/autophagy-confidence',
{ method: 'POST', body: JSON.stringify(input) },
opts
);
}
// ── Push triggers ──────────────────────────────────────────────────────────
export type PushTriggerType =
| 'streak_risk'
| 'fast_milestone'
| 'stage_transition'
| 'social_invite'
| 'weekly_digest'
| 'achievement_unlocked'
| 'refeeding_reminder'
| 'electrolyte_reminder'
| 'extended_fast_warning';
export interface PushTriggerDoc {
id: string;
productId: string;
type: PushTriggerType;
userId: string;
variables: Record<string, string>;
status: 'pending' | 'sent' | 'skipped' | 'failed';
scheduledAt: string;
createdAt: string;
}
// ── Social fasting ────────────────────────────────────────────────────────
export interface GroupFastDoc {
id: string;
userId: string;
productId: string;
sessionId: string;
memberCount: number;
status: 'active' | 'completed' | 'cancelled';
createdAt: string;
}
export function nomgapSocialListGroupFasts(
params: { limit?: number; offset?: number },
opts: NomGapClientOptions
): Promise<{ items: GroupFastDoc[]; total: number }> {
const qs = new URLSearchParams();
if (params.limit !== undefined) qs.set('limit', String(params.limit));
if (params.offset !== undefined) qs.set('offset', String(params.offset));
const q = qs.toString();
return nomgapFetch(`/social/group-fasts${q ? `?${q}` : ''}`, { method: 'GET' }, opts);
}
export function nomgapPushFire(
input: {
type: PushTriggerType;
userId: string;
variables?: Record<string, string>;
scheduledFor?: string;
},
opts: NomGapClientOptions
): Promise<PushTriggerDoc> {
return nomgapFetch('/push-triggers', { method: 'POST', body: JSON.stringify(input) }, opts);
}
export function nomgapPushGetStats(opts: NomGapClientOptions): Promise<Record<string, unknown>> {
return nomgapFetch('/push-triggers/stats', { method: 'GET' }, opts);
}
export function nomgapPushGetPending(opts: NomGapClientOptions): Promise<PushTriggerDoc[]> {
return nomgapFetch('/push-triggers/pending', { method: 'GET' }, opts);
}