feat(mcp-server): fill DOMAIN_PRODUCTS.md tool gaps — +17 tools across 6 products

- mindlyst: +memory.get (single item with full TriageResult)
- lysnrai: +transcripts.get, +orgs.get
- jarvis: +agents.create, +marketplace.listPending/certify/suspend/feature
- chronomind: +timers.create, +households.list
- nomgap: +protocols.list, +bodyStages.list
- peakpulse: +sessions.get
This commit is contained in:
saravanakumardb1 2026-03-05 13:15:22 -08:00
parent c9022665d6
commit 481760fba1
12 changed files with 379 additions and 1 deletions

View File

@ -52,6 +52,27 @@ export interface TimerDoc {
syncVersion: number;
}
export interface TimerCreateInput {
id: string;
label: string;
type: 'alarm' | 'countdown' | 'pomodoro' | 'event';
state: 'idle' | 'active' | 'paused';
urgency: 'critical' | 'important' | 'standard' | 'gentle' | 'passive';
duration?: number;
targetTime?: string;
category?: string;
cascade?: unknown;
syncVersion: number;
deviceId?: string;
}
export function chronomindTimerCreate(
input: TimerCreateInput,
opts: ChronoMindClientOptions
): Promise<TimerDoc> {
return chronomindFetch('/timers', { method: 'POST', body: JSON.stringify(input) }, opts);
}
export function chronomindTimersList(
params: { limit?: number; offset?: number; type?: string; state?: string },
opts: ChronoMindClientOptions
@ -108,6 +129,19 @@ export function chronomindRoutinesList(
return chronomindFetch(`/routines${q ? `?${q}` : ''}`, { method: 'GET' }, opts);
}
// ── Households ────────────────────────────────────────────────────────────
export function chronomindHouseholdsList(
params: { limit?: number; offset?: number },
opts: ChronoMindClientOptions
): Promise<{ items: unknown[]; 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 chronomindFetch(`/households${q ? `?${q}` : ''}`, { method: 'GET' }, opts);
}
export function chronomindTimerDelete(
timerId: string,
opts: ChronoMindClientOptions

View File

@ -61,6 +61,27 @@ export interface JarvisAgentDoc {
updatedAt: string;
}
export interface AgentCreateInput {
name: string;
role: string;
systemPrompt: string;
voiceId?: string;
coachingFramework?: string;
accentColor?: string;
welcomeMessage?: string;
sessionLength?: number;
difficultyLevel?: 'beginner' | 'intermediate' | 'advanced';
language?: string;
privacyLevel?: 'standard' | 'strict';
}
export function jarvisAgentCreate(
input: AgentCreateInput,
opts: JarvisClientOptions
): Promise<JarvisAgentDoc> {
return jarvisFetch('/jarvis/agents', { method: 'POST', body: JSON.stringify(input) }, opts);
}
export function jarvisAgentsList(
params: { limit?: number; offset?: number },
opts: JarvisClientOptions
@ -160,6 +181,50 @@ export function jarvisMemoryList(
);
}
// ── Marketplace admin ─────────────────────────────────────────────────────
export function jarvisMarketplaceListPending(
opts: JarvisClientOptions
): Promise<{ listings: unknown[]; total: number }> {
return jarvisFetch('/marketplace/admin/pending', { method: 'GET' }, opts);
}
export function jarvisMarketplaceCertify(
listingId: string,
decision: { decision: 'approved' | 'rejected'; notes?: string },
opts: JarvisClientOptions
): Promise<unknown> {
return jarvisFetch(
`/marketplace/admin/${listingId}/approve`,
{ method: 'POST', body: JSON.stringify(decision) },
opts
);
}
export function jarvisMarketplaceSuspend(
listingId: string,
reason: string,
opts: JarvisClientOptions
): Promise<unknown> {
return jarvisFetch(
`/marketplace/admin/${listingId}/suspend`,
{ method: 'POST', body: JSON.stringify({ reason }) },
opts
);
}
export function jarvisMarketplaceFeature(
listingId: string,
featured: boolean,
opts: JarvisClientOptions
): Promise<unknown> {
return jarvisFetch(
`/marketplace/admin/${listingId}/feature`,
{ method: 'POST', body: JSON.stringify({ featured }) },
opts
);
}
export function jarvisMemoryPrune(
agentId: string,
opts: JarvisClientOptions

View File

@ -66,6 +66,13 @@ export function lysnraiTranscriptsList(
return lysnraiFetch(`/transcripts${q ? `?${q}` : ''}`, { method: 'GET' }, opts);
}
export function lysnraiTranscriptGet(
id: string,
opts: LysnraiClientOptions
): Promise<TranscriptDoc> {
return lysnraiFetch(`/transcripts/${id}`, { method: 'GET' }, opts);
}
export function lysnraiTranscriptRunExtraction(
transcriptId: string,
opts: LysnraiClientOptions
@ -126,6 +133,10 @@ export interface OrgDoc {
createdAt: string;
}
export function lysnraiOrgGet(orgId: string, opts: LysnraiClientOptions): Promise<OrgDoc> {
return lysnraiFetch(`/orgs/${orgId}`, { method: 'GET' }, opts);
}
export function lysnraiOrgsList(
params: { limit?: number; offset?: number },
opts: LysnraiClientOptions

View File

@ -88,6 +88,13 @@ export function mindlystMemoryList(
return mindlystFetch(`/memory-items${q ? `?${q}` : ''}`, { method: 'GET' }, opts);
}
export function mindlystMemoryGetItem(
itemId: string,
opts: MindlystClientOptions
): Promise<MemoryItemDoc> {
return mindlystFetch(`/memory-items/${itemId}`, { method: 'GET' }, opts);
}
export function mindlystMemoryRetriage(
itemId: string,
opts: MindlystClientOptions

View File

@ -85,6 +85,22 @@ export function nomgapFastingGetWeeklyStats(
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);
}
// ── 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);
}
// ── Push triggers ──────────────────────────────────────────────────────────
export type PushTriggerType =

View File

@ -105,6 +105,13 @@ export function peakpulseSessionsList(
return peakpulseFetch(`/peak/sessions${q ? `?${q}` : ''}`, { method: 'GET' }, opts);
}
export function peakpulseSessionGet(
sessionId: string,
opts: PeakPulseClientOptions
): Promise<PeakSessionDoc> {
return peakpulseFetch(`/peak/sessions/${sessionId}`, { method: 'GET' }, opts);
}
export function peakpulseSessionExport(
sessionId: string,
opts: PeakPulseClientOptions

View File

@ -9,15 +9,43 @@ import { z } from 'zod';
import { registerTool } from '../tools/registry.js';
import { config } from '../../lib/config.js';
import {
chronomindTimerCreate,
chronomindTimersList,
chronomindTimerDelete,
chronomindRoutinesList,
chronomindSyncStatus,
chronomindHouseholdsList,
} from '../../lib/chronomind-client.js';
import type { McpToolRequest } from '../tools/types.js';
const tokenOf = (req: McpToolRequest) => req.headers.authorization?.slice(7);
// ── chronomind.timers.create ───────────────────────────────────────────────
registerTool({
name: 'chronomind.timers.create',
description:
'Create a new cloud-synced timer. The id and syncVersion must be provided by the caller (client-generated UUID and current version). Requires admin role.',
requiredRole: 'admin',
inputSchema: z.object({
id: z.string().min(1).describe('Client-generated timer UUID'),
label: z.string().min(1).describe('Timer display name'),
type: z.enum(['alarm', 'countdown', 'pomodoro', 'event']),
urgency: z.enum(['critical', 'important', 'standard', 'gentle', 'passive']).default('standard'),
duration: z.coerce.number().optional().describe('Duration in seconds (countdown/pomodoro)'),
targetTime: z.string().optional().describe('ISO 8601 target time (alarm/event)'),
category: z.string().optional().describe('Timer category label'),
syncVersion: z.coerce.number().default(1),
deviceId: z.string().optional().describe('Origin device ID for conflict detection'),
}),
async execute(args, req) {
return chronomindTimerCreate(
{ ...args, state: 'idle' },
{ token: tokenOf(req), requestId: req.id }
);
},
});
// ── chronomind.timers.list ────────────────────────────────────────────────
registerTool({
@ -88,3 +116,19 @@ registerTool({
return chronomindSyncStatus({ token: tokenOf(req), requestId: req.id });
},
});
// ── chronomind.households.list ────────────────────────────────────────────
registerTool({
name: 'chronomind.households.list',
description:
'List ChronoMind Family-tier households the authenticated user belongs to, including member list and invite codes. 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 chronomindHouseholdsList(args, { token: tokenOf(req), requestId: req.id });
},
});

View File

@ -9,6 +9,7 @@ import { z } from 'zod';
import { registerTool } from '../tools/registry.js';
import { config } from '../../lib/config.js';
import {
jarvisAgentCreate,
jarvisAgentsList,
jarvisAgentDuplicate,
jarvisSessionsList,
@ -16,11 +17,40 @@ import {
jarvisMemoryList,
jarvisMemoryPrune,
jarvisMemoryGetContext,
jarvisMarketplaceListPending,
jarvisMarketplaceCertify,
jarvisMarketplaceSuspend,
jarvisMarketplaceFeature,
} from '../../lib/jarvis-client.js';
import type { McpToolRequest } from '../tools/types.js';
const tokenOf = (req: McpToolRequest) => req.headers.authorization?.slice(7);
// ── jarvis.agents.create ─────────────────────────────────────────────────
registerTool({
name: 'jarvis.agents.create',
description:
'Create a new coaching agent with a system prompt, role, and coaching framework. Returns the created agent including its ID. Requires admin role.',
requiredRole: 'admin',
inputSchema: z.object({
name: z.string().min(1).describe('Agent display name'),
role: z.string().min(1).describe('Agent role (e.g. Career Coach, Language Tutor)'),
systemPrompt: z.string().min(1).describe('System prompt for the agent'),
coachingFramework: z.string().optional().describe('Coaching methodology (e.g. GROW, CBT)'),
voiceId: z.string().optional().describe('TTS voice identifier'),
difficultyLevel: z
.enum(['beginner', 'intermediate', 'advanced'])
.optional()
.default('intermediate'),
language: z.string().optional().default('en'),
privacyLevel: z.enum(['standard', 'strict']).optional().default('standard'),
}),
async execute(args, req) {
return jarvisAgentCreate(args, { token: tokenOf(req), requestId: req.id });
},
});
// ── jarvis.agents.list ────────────────────────────────────────────────────
registerTool({
@ -148,3 +178,75 @@ registerTool({
});
},
});
// ── jarvis.marketplace.listPending ───────────────────────────────────────
registerTool({
name: 'jarvis.marketplace.listPending',
description:
'List marketplace listings awaiting certification (status = pending_review). For admin use in the certification pipeline. Requires admin role.',
requiredRole: 'admin',
inputSchema: z.object({}),
async execute(_args, req) {
return jarvisMarketplaceListPending({ token: tokenOf(req), requestId: req.id });
},
});
// ── jarvis.marketplace.certify ───────────────────────────────────────────
registerTool({
name: 'jarvis.marketplace.certify',
description:
'Approve or reject a marketplace listing. Sets isVerified flag and notifies the author. Requires admin role.',
requiredRole: 'admin',
inputSchema: z.object({
listingId: z.string().min(1).describe('Listing ID'),
decision: z.enum(['approved', 'rejected']).describe('Certification decision'),
notes: z.string().optional().describe('Optional review notes for the author'),
}),
async execute(args, req) {
const { listingId, ...decision } = args;
return jarvisMarketplaceCertify(listingId, decision, {
token: tokenOf(req),
requestId: req.id,
});
},
});
// ── jarvis.marketplace.suspend ───────────────────────────────────────────
registerTool({
name: 'jarvis.marketplace.suspend',
description:
'Suspend a published marketplace listing (e.g. policy violation). Requires admin role.',
requiredRole: 'admin',
inputSchema: z.object({
listingId: z.string().min(1).describe('Listing ID to suspend'),
reason: z.string().min(1).describe('Suspension reason shown to the author'),
}),
async execute(args, req) {
return jarvisMarketplaceSuspend(args.listingId, args.reason, {
token: tokenOf(req),
requestId: req.id,
});
},
});
// ── jarvis.marketplace.feature ───────────────────────────────────────────
registerTool({
name: 'jarvis.marketplace.feature',
description:
'Toggle featured status on a marketplace listing (shows on homepage hero carousel). Requires admin role.',
requiredRole: 'admin',
inputSchema: z.object({
listingId: z.string().min(1).describe('Listing ID'),
featured: z.boolean().describe('true = feature, false = unfeature'),
}),
async execute(args, req) {
return jarvisMarketplaceFeature(args.listingId, args.featured, {
token: tokenOf(req),
requestId: req.id,
});
},
});

View File

@ -11,16 +11,33 @@ import { registerTool } from '../tools/registry.js';
import { config } from '../../lib/config.js';
import {
lysnraiTranscriptsList,
lysnraiTranscriptGet,
lysnraiTranscriptRunExtraction,
lysnraiSttGetBackendStatus,
lysnraiSessionsList,
lysnraiOrgsList,
lysnraiOrgGet,
lysnraiApiTokensList,
} from '../../lib/lysnrai-client.js';
import type { McpToolRequest } from '../tools/types.js';
const tokenOf = (req: McpToolRequest) => req.headers.authorization?.slice(7);
// ── lysnrai.transcripts.get ───────────────────────────────────────────────
registerTool({
name: 'lysnrai.transcripts.get',
description:
'Get a single transcript by ID, including extractions[] and extractionMetadata if extraction has run. Requires admin role.',
requiredRole: 'admin',
inputSchema: z.object({
transcriptId: z.string().min(1).describe('Transcript ID'),
}),
async execute(args, req) {
return lysnraiTranscriptGet(args.transcriptId, { token: tokenOf(req), requestId: req.id });
},
});
// ── lysnrai.transcripts.list ──────────────────────────────────────────────
registerTool({
@ -101,6 +118,21 @@ registerTool({
},
});
// ── lysnrai.orgs.get ─────────────────────────────────────────────────────
registerTool({
name: 'lysnrai.orgs.get',
description:
'Get a single organisation by ID including member count, vocabulary, and shared instructions. Requires admin role.',
requiredRole: 'admin',
inputSchema: z.object({
orgId: z.string().min(1).describe('Organisation ID'),
}),
async execute(args, req) {
return lysnraiOrgGet(args.orgId, { token: tokenOf(req), requestId: req.id });
},
});
// ── lysnrai.apiTokens.list ────────────────────────────────────────────────
registerTool({

View File

@ -11,6 +11,7 @@ import { registerTool } from '../tools/registry.js';
import { config } from '../../lib/config.js';
import {
mindlystMemoryList,
mindlystMemoryGetItem,
mindlystMemoryRetriage,
mindlystMemoryPatch,
mindlystMemoryReassign,
@ -25,6 +26,21 @@ import type { McpToolRequest } from '../tools/types.js';
const tokenOf = (req: McpToolRequest) => req.headers.authorization?.slice(7);
// ── mindlyst.memory.get ───────────────────────────────────────────────────
registerTool({
name: 'mindlyst.memory.get',
description:
'Get a single memory item by ID, including its full TriageResult (urgencyScore, emotionScore, confidenceScore, entities, suggestedActions). Requires admin role.',
requiredRole: 'admin',
inputSchema: z.object({
itemId: z.string().min(1).describe('Memory item ID'),
}),
async execute(args, req) {
return mindlystMemoryGetItem(args.itemId, { token: tokenOf(req), requestId: req.id });
},
});
// ── mindlyst.memory.list ──────────────────────────────────────────────────
registerTool({

View File

@ -12,6 +12,8 @@ import {
nomgapFastingSessionsList,
nomgapFastingGetStats,
nomgapFastingGetWeeklyStats,
nomgapProtocolsList,
nomgapBodyStagesList,
nomgapPushFire,
nomgapPushGetStats,
nomgapPushGetPending,
@ -130,6 +132,32 @@ registerTool({
},
});
// ── nomgap.protocols.list ──────────────────────────────────────────────────
registerTool({
name: 'nomgap.protocols.list',
description:
'List all fasting protocols (built-in + user custom). Includes fastHours, eatHours, difficulty, and type. Requires admin role.',
requiredRole: 'admin',
inputSchema: z.object({}),
async execute(_args, req) {
return nomgapProtocolsList({ token: tokenOf(req), requestId: req.id });
},
});
// ── nomgap.bodyStages.list ───────────────────────────────────────────────
registerTool({
name: 'nomgap.bodyStages.list',
description:
'List all 6 fasting body stages (fed, early_fast, fasted, ketosis, deep_autophagy, extended) with hour thresholds and visualization metadata. Public endpoint — no auth needed.',
requiredRole: 'admin',
inputSchema: z.object({}),
async execute(_args, req) {
return nomgapBodyStagesList({ requestId: req.id });
},
});
// ── nomgap.push.pending ───────────────────────────────────────────────────
registerTool({

View File

@ -10,6 +10,7 @@ import { registerTool } from '../tools/registry.js';
import { config } from '../../lib/config.js';
import {
peakpulseSessionsList,
peakpulseSessionGet,
peakpulseSessionExport,
peakpulseGetStats,
peakpulseRouteGet,
@ -70,7 +71,22 @@ registerTool({
},
});
// ── peakpulse.routes.get ──────────────────────────────────────────────────
// ── peakpulse.sessions.get ──────────────────────────────────────────────────
registerTool({
name: 'peakpulse.sessions.get',
description:
'Get a single adventure session by ID including activityType, GPS trackPoints summary, ski/hike metrics, weather snapshot, and haptic events. Requires admin role.',
requiredRole: 'admin',
inputSchema: z.object({
sessionId: z.string().min(1).describe('Session ID'),
}),
async execute(args, req) {
return peakpulseSessionGet(args.sessionId, { token: tokenOf(req), requestId: req.id });
},
});
// ── peakpulse.routes.get ────────────────────────────────────────────────────
registerTool({
name: 'peakpulse.routes.get',