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
237 lines
9.4 KiB
TypeScript
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);
|
|
},
|
|
});
|