learning_ai_common_plat/services/mcp-server/src/modules/lysnrai/lysnrai-tools.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

237 lines
9.4 KiB
TypeScript

/**
* LysnrAI MCP tools — lysnrai.transcripts.*, lysnrai.stt.*, lysnrai.sessions.*,
* lysnrai.orgs.*, lysnrai.apiTokens.*
*
* Backed by: lysnrai-backend (port 4015).
* All tools require admin role.
*/
import { z } from 'zod';
import { registerTool } from '../tools/registry.js';
import { config } from '../../lib/config.js';
import {
lysnraiTranscriptsList,
lysnraiTranscriptGet,
lysnraiTranscriptRunExtraction,
lysnraiSttGetBackendStatus,
lysnraiSessionGet,
lysnraiSessionsList,
lysnraiOrgsList,
lysnraiOrgGet,
lysnraiApiTokensList,
lysnraiApiTokenCreate,
lysnraiApiTokenRevoke,
lysnraiSessionsStats,
lysnraiTranscriptsExportBatch,
} 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({
name: 'lysnrai.transcripts.list',
description: 'List dictation transcripts for the authenticated user. 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 lysnraiTranscriptsList(args, { token: tokenOf(req), requestId: req.id });
},
});
// ── lysnrai.transcripts.runExtraction ────────────────────────────────────
registerTool({
name: 'lysnrai.transcripts.runExtraction',
description:
'Run extraction-service enrichment on a transcript — populates extractions[] and extractedAt. Best-effort: returns partial if extraction-service is unavailable. Requires admin role.',
requiredRole: 'admin',
inputSchema: z.object({
transcriptId: z.string().min(1).describe('Transcript ID (trx_... prefix)'),
}),
async execute(args, req) {
return lysnraiTranscriptRunExtraction(args.transcriptId, {
token: tokenOf(req),
requestId: req.id,
});
},
});
// ── lysnrai.stt.getBackendStatus ──────────────────────────────────────────
registerTool({
name: 'lysnrai.stt.getBackendStatus',
description:
'Report which speech-to-text backend is active: azure (Azure Speech SDK) or whisper (local Whisper model). Useful for ops health checks. Requires admin role.',
requiredRole: 'admin',
inputSchema: z.object({}),
async execute(_args, req) {
return lysnraiSttGetBackendStatus({ token: tokenOf(req), requestId: req.id });
},
});
// ── lysnrai.sessions.get ──────────────────────────────────────────────────
registerTool({
name: 'lysnrai.sessions.get',
description:
'Get a single dictation session by ID including its entries[], composedText, compositionHistory, and status. Requires admin role.',
requiredRole: 'admin',
inputSchema: z.object({
sessionId: z.string().min(1).describe('Session ID (ses_… prefix)'),
}),
async execute(args, req) {
return lysnraiSessionGet(args.sessionId, { token: tokenOf(req), requestId: req.id });
},
});
// ── lysnrai.sessions.list ─────────────────────────────────────────────────
registerTool({
name: 'lysnrai.sessions.list',
description: 'List dictation sessions for the authenticated user. Requires admin role.',
requiredRole: 'admin',
inputSchema: z.object({
status: z
.enum(['active', 'composed', 'archived'])
.optional()
.describe('Filter by session status'),
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 lysnraiSessionsList(args, { token: tokenOf(req), requestId: req.id });
},
});
// ── lysnrai.orgs.list ─────────────────────────────────────────────────────
registerTool({
name: 'lysnrai.orgs.list',
description: 'List organisations for the authenticated user. 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 lysnraiOrgsList(args, { token: tokenOf(req), requestId: req.id });
},
});
// ── 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.revoke ───────────────────────────────────────────────
registerTool({
name: 'lysnrai.apiTokens.revoke',
description:
'Revoke (soft-delete) an API token by ID — sets status to "revoked" so it can no longer authenticate. Irreversible. Requires admin role.',
requiredRole: 'admin',
inputSchema: z.object({
tokenId: z.string().min(1).describe('API token ID (tok_… prefix)'),
}),
async execute(args, req) {
return lysnraiApiTokenRevoke(args.tokenId, { token: tokenOf(req), requestId: req.id });
},
});
// ── lysnrai.apiTokens.list ─────────────────────────────────────────────────
registerTool({
name: 'lysnrai.apiTokens.list',
description: 'List API tokens for the authenticated user. 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 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);
},
});