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
212 lines
8.3 KiB
TypeScript
212 lines
8.3 KiB
TypeScript
/**
|
|
* ChronoMind MCP tools — chronomind.timers.*, chronomind.routines.*, chronomind.syncStatus
|
|
*
|
|
* Backed by: chronomind-backend (port 4011).
|
|
* All tools require admin role.
|
|
*/
|
|
|
|
import { z } from 'zod';
|
|
import { registerTool } from '../tools/registry.js';
|
|
import { config } from '../../lib/config.js';
|
|
import {
|
|
chronomindTimerCreate,
|
|
chronomindTimersList,
|
|
chronomindTimerDelete,
|
|
chronomindRoutineGet,
|
|
chronomindRoutinesList,
|
|
chronomindSyncStatus,
|
|
chronomindHouseholdsList,
|
|
chronomindSharedTimerShare,
|
|
} 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({
|
|
name: 'chronomind.timers.list',
|
|
description:
|
|
'List cloud-synced timers for the authenticated user. Filter by type (alarm/countdown/pomodoro/event) or state (idle/active/paused/warning/fired/completed). Requires admin role.',
|
|
requiredRole: 'admin',
|
|
inputSchema: z.object({
|
|
type: z
|
|
.enum(['alarm', 'countdown', 'pomodoro', 'event'])
|
|
.optional()
|
|
.describe('Filter by timer type'),
|
|
state: z
|
|
.enum(['idle', 'active', 'paused', 'warning', 'fired', 'completed'])
|
|
.optional()
|
|
.describe('Filter by timer state'),
|
|
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 chronomindTimersList(args, { token: tokenOf(req), requestId: req.id });
|
|
},
|
|
});
|
|
|
|
// ── chronomind.timers.delete ──────────────────────────────────────────────
|
|
|
|
registerTool({
|
|
name: 'chronomind.timers.delete',
|
|
description: 'Delete a timer by ID. Use with care — deletion is permanent. Requires admin role.',
|
|
requiredRole: 'admin',
|
|
inputSchema: z.object({
|
|
timerId: z.string().min(1).describe('Timer ID to delete'),
|
|
}),
|
|
async execute(args, req) {
|
|
return chronomindTimerDelete(args.timerId, { token: tokenOf(req), requestId: req.id });
|
|
},
|
|
});
|
|
|
|
// ── chronomind.routines.list ──────────────────────────────────────────────
|
|
|
|
registerTool({
|
|
name: 'chronomind.routines.list',
|
|
description:
|
|
'List cloud-synced routines for the authenticated user. Optionally filter to template routines only. Requires admin role.',
|
|
requiredRole: 'admin',
|
|
inputSchema: z.object({
|
|
isTemplate: z
|
|
.boolean()
|
|
.optional()
|
|
.describe('true = only templates, false = only user routines'),
|
|
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 chronomindRoutinesList(args, { token: tokenOf(req), requestId: req.id });
|
|
},
|
|
});
|
|
|
|
// ── chronomind.syncStatus ─────────────────────────────────────────────────
|
|
|
|
registerTool({
|
|
name: 'chronomind.syncStatus',
|
|
description:
|
|
'Instant sync health check: total timers, active count, pending count, unsynced count, and last sync timestamp. Requires admin role.',
|
|
requiredRole: 'admin',
|
|
inputSchema: z.object({}),
|
|
async execute(_args, req) {
|
|
return chronomindSyncStatus({ token: tokenOf(req), requestId: req.id });
|
|
},
|
|
});
|
|
|
|
// ── 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',
|
|
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 });
|
|
},
|
|
});
|