/** * 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, } 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.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 }); }, });