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
This commit is contained in:
saravanakumardb1 2026-03-05 18:19:04 -08:00
parent 1da3394caf
commit 3f296a8e72
9 changed files with 360 additions and 2 deletions

View File

@ -117,6 +117,13 @@ export interface RoutineDoc {
syncVersion: number;
}
export function chronomindRoutineGet(
routineId: string,
opts: ChronoMindClientOptions
): Promise<RoutineDoc> {
return chronomindFetch(`/routines/${routineId}`, { method: 'GET' }, opts);
}
export function chronomindRoutinesList(
params: { limit?: number; offset?: number; isTemplate?: boolean },
opts: ChronoMindClientOptions
@ -142,6 +149,18 @@ export function chronomindHouseholdsList(
return chronomindFetch(`/households${q ? `?${q}` : ''}`, { method: 'GET' }, opts);
}
export function chronomindSharedTimerShare(
timerId: string,
targets: { userId?: string; householdId?: string }[],
opts: ChronoMindClientOptions
): Promise<{ sharedTimerId: string; timerId: string; targetCount: number; sharedAt: string }> {
return chronomindFetch(
'/shared-timers',
{ method: 'POST', body: JSON.stringify({ timerId, targets }) },
opts
);
}
export function chronomindTimerDelete(
timerId: string,
opts: ChronoMindClientOptions

View File

@ -177,9 +177,31 @@ export function lysnraiApiTokensList(
return lysnraiFetch(`/api-tokens${q ? `?${q}` : ''}`, { method: 'GET' }, opts);
}
export function lysnraiApiTokenCreate(
input: { name: string },
opts: LysnraiClientOptions
): Promise<ApiTokenDoc & { rawToken: string }> {
return lysnraiFetch('/api-tokens', { method: 'POST', body: JSON.stringify(input) }, opts);
}
export function lysnraiApiTokenRevoke(
tokenId: string,
opts: LysnraiClientOptions
): Promise<{ success: boolean }> {
return lysnraiFetch(`/api-tokens/${tokenId}`, { method: 'DELETE' }, opts);
}
export function lysnraiSessionsStats(opts: LysnraiClientOptions): Promise<Record<string, unknown>> {
return lysnraiFetch('/sessions/stats', { method: 'GET' }, opts);
}
export function lysnraiTranscriptsExportBatch(
params: { format?: 'json' | 'txt'; limit?: number },
opts: LysnraiClientOptions
): Promise<{ exportUrl: string; count: number; format: string; generatedAt: string }> {
const qs = new URLSearchParams();
if (params.format) qs.set('format', params.format);
if (params.limit !== undefined) qs.set('limit', String(params.limit));
const q = qs.toString();
return lysnraiFetch(`/transcripts/export${q ? `?${q}` : ''}`, { method: 'GET' }, opts);
}

View File

@ -61,6 +61,13 @@ export interface FastingSessionDoc {
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
@ -100,6 +107,13 @@ export function nomgapProtocolsList(
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(
@ -168,6 +182,29 @@ export interface PushTriggerDoc {
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;

View File

@ -144,9 +144,30 @@ export interface PeakRouteDoc {
updatedAt: string;
}
export function peakpulseRoutesList(
params: { limit?: number; offset?: number },
opts: PeakPulseClientOptions
): Promise<{ items: PeakRouteDoc[]; 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 peakpulseFetch(`/peak/routes${q ? `?${q}` : ''}`, { method: 'GET' }, opts);
}
export function peakpulseRouteGet(
sessionId: string,
opts: PeakPulseClientOptions
): Promise<PeakRouteDoc> {
return peakpulseFetch(`/peak/routes/${sessionId}`, { method: 'GET' }, opts);
}
export function peakpulseSyncStatus(opts: PeakPulseClientOptions): Promise<{
userId: string;
productId: string;
pendingUploadCount: number;
lastSyncedAt: string | null;
oldestPendingAt: string | null;
}> {
return peakpulseFetch('/peak/sync/status', { method: 'GET' }, opts);
}

View File

@ -12,9 +12,11 @@ import {
chronomindTimerCreate,
chronomindTimersList,
chronomindTimerDelete,
chronomindRoutineGet,
chronomindRoutinesList,
chronomindSyncStatus,
chronomindHouseholdsList,
chronomindSharedTimerShare,
} from '../../lib/chronomind-client.js';
import type { McpToolRequest } from '../tools/types.js';
@ -117,7 +119,82 @@ registerTool({
},
});
// ── chronomind.households.list ────────────────────────────────────────────
// ── chronomind.sharedTimers.share ──────────────────────────────────────────
registerTool({
name: 'chronomind.sharedTimers.share',
description:
'Share a timer with one or more household members or specific user IDs. Creates a shared-timer record in the shared-timers module. Requires admin role.',
requiredRole: 'admin',
inputSchema: z.object({
timerId: z.string().min(1).describe('ID of the timer to share'),
targets: z
.array(
z.object({
userId: z.string().optional().describe('Target user ID'),
householdId: z
.string()
.optional()
.describe('Target household ID (shares with all members)'),
})
)
.min(1)
.describe('Recipients — provide userId or householdId for each target'),
}),
async execute(args, req) {
return chronomindSharedTimerShare(args.timerId, args.targets, {
token: tokenOf(req),
requestId: req.id,
});
},
});
// ── chronomind.routines.validate ─────────────────────────────────────────
registerTool({
name: 'chronomind.routines.validate',
description:
'Validate a routine for structural integrity: checks that total duration does not exceed the maximum, that all steps have non-zero duration, and that the routine has at least one step. Returns pass/fail with a list of issues. Requires admin role.',
requiredRole: 'admin',
inputSchema: z.object({
routineId: z.string().min(1).describe('Routine ID to validate'),
maxDurationMinutes: z.coerce
.number()
.min(1)
.max(1440)
.default(480)
.describe('Maximum allowed total duration in minutes (default 480 = 8h)'),
}),
async execute(args, req) {
let routine;
try {
routine = await chronomindRoutineGet(args.routineId, {
token: tokenOf(req),
requestId: req.id,
});
} catch {
return { valid: false, routineId: args.routineId, issues: ['Routine not found'] };
}
const issues: string[] = [];
if (!routine.steps || routine.steps.length === 0) issues.push('Routine has no steps');
if (routine.totalDurationMinutes > args.maxDurationMinutes) {
issues.push(
`Total duration ${routine.totalDurationMinutes}min exceeds max ${args.maxDurationMinutes}min`
);
}
return {
valid: issues.length === 0,
routineId: routine.id,
routineName: routine.name,
stepCount: routine.steps.length,
totalDurationMinutes: routine.totalDurationMinutes,
maxDurationMinutes: args.maxDurationMinutes,
issues,
};
},
});
// ── chronomind.households.list ──────────────────────────────────────────────
registerTool({
name: 'chronomind.households.list',

View File

@ -19,7 +19,10 @@ import {
lysnraiOrgsList,
lysnraiOrgGet,
lysnraiApiTokensList,
lysnraiApiTokenCreate,
lysnraiApiTokenRevoke,
lysnraiSessionsStats,
lysnraiTranscriptsExportBatch,
} from '../../lib/lysnrai-client.js';
import type { McpToolRequest } from '../tools/types.js';
@ -179,3 +182,55 @@ registerTool({
return lysnraiApiTokensList(args, { token: tokenOf(req), requestId: req.id });
},
});
// ── lysnrai.sessions.stats ────────────────────────────────────────────────
registerTool({
name: 'lysnrai.sessions.stats',
description:
'Get aggregated dictation session statistics for the authenticated user: total sessions, composed count, average word count, and last session date. Requires admin role.',
requiredRole: 'admin',
inputSchema: z.object({}),
async execute(_args, req) {
return lysnraiSessionsStats({ token: tokenOf(req), requestId: req.id });
},
});
// ── lysnrai.transcripts.exportBatch ──────────────────────────────────────
registerTool({
name: 'lysnrai.transcripts.exportBatch',
description:
'Batch-export transcripts for the authenticated user as JSON or plain text. Returns a signed export URL, item count, and format. Requires admin role.',
requiredRole: 'admin',
inputSchema: z.object({
format: z.enum(['json', 'txt']).default('json').describe('Export format'),
limit: z.coerce
.number()
.min(1)
.max(config.QUERY_MAX_LIMIT)
.default(config.QUERY_DEFAULT_LIMIT)
.describe('Max transcripts to include'),
}),
async execute(args, req) {
return lysnraiTranscriptsExportBatch(args, { token: tokenOf(req), requestId: req.id });
},
});
// ── lysnrai.apiTokens.rotate ──────────────────────────────────────────────
registerTool({
name: 'lysnrai.apiTokens.rotate',
description:
'Rotate an API token: revokes the existing token and issues a fresh one with the same name. Returns the new raw token (shown once only). Requires admin role.',
requiredRole: 'admin',
inputSchema: z.object({
tokenId: z.string().min(1).describe('Existing API token ID to rotate'),
name: z.string().min(1).describe('Name for the replacement token'),
}),
async execute(args, req) {
const opts = { token: tokenOf(req), requestId: req.id };
await lysnraiApiTokenRevoke(args.tokenId, opts);
return lysnraiApiTokenCreate({ name: args.name }, opts);
},
});

View File

@ -16,6 +16,7 @@ import {
mindlystMemoryPatch,
mindlystMemoryReassign,
mindlystBrainsList,
mindlystBriefCreate,
mindlystBriefsGetToday,
mindlystBriefsList,
mindlystStreaksGet,
@ -192,6 +193,49 @@ registerTool({
},
});
// ── mindlyst.briefs.generate ────────────────────────────────────────────────
registerTool({
name: 'mindlyst.briefs.generate',
description:
'Trigger generation of a Daily Brief for the authenticated user for a specific date. Assembles greeting, priorityItems[], and brainSummaries from recent memory activity. Requires admin role.',
requiredRole: 'admin',
inputSchema: z.object({
date: z
.string()
.regex(/^\d{4}-\d{2}-\d{2}$/)
.describe('Target date in YYYY-MM-DD format (defaults to today if omitted)')
.optional(),
}),
async execute(args, req) {
const date = args.date ?? new Date().toISOString().slice(0, 10);
return mindlystBriefCreate({ date }, { token: tokenOf(req), requestId: req.id });
},
});
// ── mindlyst.memory.getTriageResult ──────────────────────────────────────────
registerTool({
name: 'mindlyst.memory.getTriageResult',
description:
'Get just the TriageResult sub-document for a memory item: contentType, urgencyScore (01), emotionScore (1+1), confidenceScore (01), suggestedBrainId, entities[], suggestedActions[]. Lighter than mindlyst.memory.get when only triage scoring is needed. Requires admin role.',
requiredRole: 'admin',
inputSchema: z.object({
itemId: z.string().min(1).describe('Memory item ID'),
}),
async execute(args, req) {
const item = await mindlystMemoryGetItem(args.itemId, {
token: tokenOf(req),
requestId: req.id,
});
return {
itemId: item.id,
brainIds: item.brainIds,
triageResult: item.triageResult ?? null,
};
},
});
// ── mindlyst.extractions.run ──────────────────────────────────────────────
registerTool({

View File

@ -9,11 +9,14 @@ import { z } from 'zod';
import { registerTool } from '../tools/registry.js';
import { config } from '../../lib/config.js';
import {
nomgapFastingCreateSession,
nomgapFastingSessionsList,
nomgapFastingSessionGet,
nomgapFastingGetStats,
nomgapFastingGetWeeklyStats,
nomgapProtocolsList,
nomgapProtocolGet,
nomgapSocialListGroupFasts,
nomgapBodyStagesList,
nomgapAutophagyConfidence,
nomgapPushFire,
@ -222,6 +225,55 @@ registerTool({
},
});
// ── nomgap.fasting.createSession ────────────────────────────────────────────
registerTool({
name: 'nomgap.fasting.createSession',
description:
'Start a new fasting session for the authenticated user with a given protocol. Returns the new session document including startedAt and targetDurationMs. Requires admin role.',
requiredRole: 'admin',
inputSchema: z.object({
protocolId: z.string().min(1).describe('Protocol ID to use for this session'),
}),
async execute(args, req) {
return nomgapFastingCreateSession(
{ protocolId: args.protocolId },
{ token: tokenOf(req), requestId: req.id }
);
},
});
// ── nomgap.protocols.get ─────────────────────────────────────────────────────
registerTool({
name: 'nomgap.protocols.get',
description:
'Get a single fasting protocol by ID including its name, targetHours, description, steps, and default variables. Requires admin role.',
requiredRole: 'admin',
inputSchema: z.object({
protocolId: z.string().min(1).describe('Fasting protocol ID'),
}),
async execute(args, req) {
return nomgapProtocolGet(args.protocolId, { token: tokenOf(req), requestId: req.id });
},
});
// ── nomgap.social.listGroupFasts ─────────────────────────────────────────────
registerTool({
name: 'nomgap.social.listGroupFasts',
description:
'List group fasting sessions the authenticated user has participated in or created. Returns sessionId, memberCount, and status (active/completed/cancelled). Requires admin role.',
requiredRole: 'admin',
inputSchema: z.object({
limit: z.coerce.number().min(1).max(config.QUERY_MAX_LIMIT).default(config.QUERY_DEFAULT_LIMIT),
offset: z.coerce.number().min(0).default(0),
}),
async execute(args, req) {
return nomgapSocialListGroupFasts(args, { token: tokenOf(req), requestId: req.id });
},
});
// ── nomgap.push.pending ───────────────────────────────────────────────────
registerTool({

View File

@ -13,7 +13,9 @@ import {
peakpulseSessionGet,
peakpulseSessionExport,
peakpulseGetStats,
peakpulseRoutesList,
peakpulseRouteGet,
peakpulseSyncStatus,
} from '../../lib/peakpulse-client.js';
import type { McpToolRequest } from '../tools/types.js';
@ -86,6 +88,22 @@ registerTool({
},
});
// ── peakpulse.routes.list ───────────────────────────────────────────────────
registerTool({
name: 'peakpulse.routes.list',
description:
'List GPS route documents for the authenticated user. Each route contains track points, haptic milestone events, and a bounding box. Requires admin role.',
requiredRole: 'admin',
inputSchema: z.object({
limit: z.coerce.number().min(1).max(config.QUERY_MAX_LIMIT).default(config.QUERY_DEFAULT_LIMIT),
offset: z.coerce.number().min(0).default(0),
}),
async execute(args, req) {
return peakpulseRoutesList(args, { token: tokenOf(req), requestId: req.id });
},
});
// ── peakpulse.routes.get ────────────────────────────────────────────────────
registerTool({
@ -101,7 +119,20 @@ registerTool({
},
});
// ── peakpulse.weather.getSnapshot ───────────────────────────────────────────
// ── peakpulse.syncStatus ─────────────────────────────────────────────────────
registerTool({
name: 'peakpulse.syncStatus',
description:
'Check the PeakPulse offline upload queue: pending session count, last successful sync timestamp, and oldest pending item age. Useful for diagnosing sync failures on iOS. Requires admin role.',
requiredRole: 'admin',
inputSchema: z.object({}),
async execute(_args, req) {
return peakpulseSyncStatus({ token: tokenOf(req), requestId: req.id });
},
});
// ── peakpulse.weather.getSnapshot ───────────────────────────────────────────────
registerTool({
name: 'peakpulse.weather.getSnapshot',