feat(mcp-server): Phase 3 — product namespaces for MindLyst, LysnrAI, JarvisJr (22 new tools)
This commit is contained in:
parent
852cb18a5c
commit
f780f0b409
@ -7,6 +7,11 @@ LOG_LEVEL=info
|
||||
PLATFORM_SERVICE_URL=http://localhost:4003
|
||||
EXTRACTION_SERVICE_URL=http://localhost:4005
|
||||
|
||||
# Product-specific backend URLs (Phase 3 product namespaces)
|
||||
MINDLYST_BACKEND_URL=http://localhost:4014
|
||||
LYSNRAI_BACKEND_URL=http://localhost:4015
|
||||
JARVISJR_BACKEND_URL=http://localhost:4012
|
||||
|
||||
# Auth — same JWT_SECRET as platform-service (tokens issued there are validated here)
|
||||
JWT_SECRET=change-me-in-production
|
||||
|
||||
|
||||
@ -9,6 +9,10 @@ const envSchema = z.object({
|
||||
JWT_SECRET: z.string().min(1, 'JWT_SECRET is required'),
|
||||
PLATFORM_SERVICE_URL: z.string().default('http://localhost:4003'),
|
||||
EXTRACTION_SERVICE_URL: z.string().default('http://localhost:4005'),
|
||||
/** Product-specific backend URLs */
|
||||
MINDLYST_BACKEND_URL: z.string().default('http://localhost:4014'),
|
||||
LYSNRAI_BACKEND_URL: z.string().default('http://localhost:4015'),
|
||||
JARVISJR_BACKEND_URL: z.string().default('http://localhost:4012'),
|
||||
/** Max items returned per tool call query (hard cap) */
|
||||
QUERY_MAX_LIMIT: z.coerce.number().default(100),
|
||||
/** Default items per tool call query */
|
||||
|
||||
177
services/mcp-server/src/lib/jarvis-client.ts
Normal file
177
services/mcp-server/src/lib/jarvis-client.ts
Normal file
@ -0,0 +1,177 @@
|
||||
/**
|
||||
* JarvisJr backend client — typed HTTP wrappers for the jarvisjr-backend (port 4012).
|
||||
*
|
||||
* Auth: Bearer token from the caller's JWT (same JWT_SECRET as platform-service).
|
||||
*/
|
||||
|
||||
import { config } from './config.js';
|
||||
|
||||
export interface JarvisClientOptions {
|
||||
token?: string;
|
||||
requestId?: string;
|
||||
}
|
||||
|
||||
// ── Shared fetch helper ────────────────────────────────────────────────────
|
||||
|
||||
async function jarvisFetch<T>(
|
||||
path: string,
|
||||
init: RequestInit,
|
||||
opts: JarvisClientOptions
|
||||
): Promise<T> {
|
||||
const headers: Record<string, string> = {
|
||||
'Content-Type': 'application/json',
|
||||
...(opts.token ? { Authorization: `Bearer ${opts.token}` } : {}),
|
||||
...(opts.requestId ? { 'x-request-id': opts.requestId } : {}),
|
||||
};
|
||||
const res = await fetch(`${config.JARVISJR_BACKEND_URL}${path}`, {
|
||||
...init,
|
||||
headers: { ...((init.headers as Record<string, string>) ?? {}), ...headers },
|
||||
signal: AbortSignal.timeout(15_000),
|
||||
});
|
||||
if (!res.ok) {
|
||||
const body = await res.text().catch(() => '');
|
||||
throw new Error(`jarvisjr-backend ${init.method ?? 'GET'} ${path} → ${res.status}: ${body}`);
|
||||
}
|
||||
return res.json() as Promise<T>;
|
||||
}
|
||||
|
||||
// ── Agents ─────────────────────────────────────────────────────────────────
|
||||
|
||||
export interface JarvisAgentDoc {
|
||||
id: string;
|
||||
userId: string;
|
||||
productId: string;
|
||||
name: string;
|
||||
role: string;
|
||||
systemPrompt: string;
|
||||
voiceId?: string;
|
||||
coachingFramework?: string;
|
||||
accentColor?: string;
|
||||
welcomeMessage?: string;
|
||||
sessionLength?: number;
|
||||
difficultyLevel?: string;
|
||||
language?: string;
|
||||
privacyLevel?: string;
|
||||
checkInSchedule?: string;
|
||||
isTemplate?: boolean;
|
||||
templateSource?: string;
|
||||
totalSessions: number;
|
||||
lastSessionAt: string | null;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
export function jarvisAgentsList(
|
||||
params: { limit?: number; offset?: number },
|
||||
opts: JarvisClientOptions
|
||||
): Promise<{ agents: JarvisAgentDoc[]; 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 jarvisFetch(`/jarvis/agents${q ? `?${q}` : ''}`, { method: 'GET' }, opts);
|
||||
}
|
||||
|
||||
export function jarvisAgentDuplicate(
|
||||
agentId: string,
|
||||
opts: JarvisClientOptions
|
||||
): Promise<JarvisAgentDoc> {
|
||||
return jarvisFetch(`/jarvis/agents/${agentId}/duplicate`, { method: 'POST' }, opts);
|
||||
}
|
||||
|
||||
// ── Sessions ───────────────────────────────────────────────────────────────
|
||||
|
||||
export interface JarvisSessionDoc {
|
||||
id: string;
|
||||
userId: string;
|
||||
productId: string;
|
||||
agentId: string;
|
||||
mode: string;
|
||||
status: 'active' | 'completed';
|
||||
summary?: string;
|
||||
coachingNotes?: string[];
|
||||
skillMetrics?: Record<string, number>;
|
||||
duration?: number;
|
||||
messageCount: number;
|
||||
createdAt: string;
|
||||
completedAt: string | null;
|
||||
}
|
||||
|
||||
export interface JarvisSessionStats {
|
||||
totalSessions: number;
|
||||
totalDurationMinutes: number;
|
||||
currentStreak: number;
|
||||
longestStreak: number;
|
||||
perAgent: Record<string, { sessions: number; avgDuration: number }>;
|
||||
}
|
||||
|
||||
export function jarvisSessionsList(
|
||||
params: { limit?: number; offset?: number; agentId?: string },
|
||||
opts: JarvisClientOptions
|
||||
): Promise<{ sessions: JarvisSessionDoc[]; 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));
|
||||
if (params.agentId) qs.set('agentId', params.agentId);
|
||||
const q = qs.toString();
|
||||
return jarvisFetch(`/jarvis/sessions${q ? `?${q}` : ''}`, { method: 'GET' }, opts);
|
||||
}
|
||||
|
||||
export function jarvisSessionsGetStats(opts: JarvisClientOptions): Promise<JarvisSessionStats> {
|
||||
return jarvisFetch('/jarvis/sessions/stats', { method: 'GET' }, opts);
|
||||
}
|
||||
|
||||
// ── Memory ─────────────────────────────────────────────────────────────────
|
||||
|
||||
export interface JarvisMemoryDoc {
|
||||
id: string;
|
||||
agentId: string;
|
||||
userId: string;
|
||||
productId: string;
|
||||
sessionId?: string;
|
||||
type: 'skill_note' | 'preference' | 'goal' | 'context' | 'exercise';
|
||||
content: string;
|
||||
importance: number;
|
||||
tags?: string[];
|
||||
createdAt: string;
|
||||
expiresAt?: string;
|
||||
}
|
||||
|
||||
export function jarvisMemoryList(
|
||||
agentId: string,
|
||||
params: {
|
||||
type?: string;
|
||||
minImportance?: number;
|
||||
limit?: number;
|
||||
offset?: number;
|
||||
},
|
||||
opts: JarvisClientOptions
|
||||
): Promise<{ memories: JarvisMemoryDoc[]; total: number }> {
|
||||
const qs = new URLSearchParams();
|
||||
if (params.type) qs.set('type', params.type);
|
||||
if (params.minImportance !== undefined) qs.set('minImportance', String(params.minImportance));
|
||||
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 jarvisFetch(
|
||||
`/jarvis/agents/${agentId}/memory${q ? `?${q}` : ''}`,
|
||||
{ method: 'GET' },
|
||||
opts
|
||||
);
|
||||
}
|
||||
|
||||
export function jarvisMemoryPrune(
|
||||
agentId: string,
|
||||
opts: JarvisClientOptions
|
||||
): Promise<{ pruned: number }> {
|
||||
return jarvisFetch(`/jarvis/agents/${agentId}/memory/prune`, { method: 'POST' }, opts);
|
||||
}
|
||||
|
||||
export function jarvisMemoryGetContext(
|
||||
agentId: string,
|
||||
limit: number | undefined,
|
||||
opts: JarvisClientOptions
|
||||
): Promise<{ memories: JarvisMemoryDoc[]; count: number }> {
|
||||
const qs = limit !== undefined ? `?limit=${limit}` : '';
|
||||
return jarvisFetch(`/jarvis/agents/${agentId}/memory/context${qs}`, { method: 'GET' }, opts);
|
||||
}
|
||||
167
services/mcp-server/src/lib/lysnrai-client.ts
Normal file
167
services/mcp-server/src/lib/lysnrai-client.ts
Normal file
@ -0,0 +1,167 @@
|
||||
/**
|
||||
* LysnrAI backend client — typed HTTP wrappers for the lysnrai-backend (port 4015).
|
||||
*
|
||||
* Auth: Bearer token from the caller's JWT (same JWT_SECRET as platform-service).
|
||||
*/
|
||||
|
||||
import { config } from './config.js';
|
||||
|
||||
export interface LysnraiClientOptions {
|
||||
token?: string;
|
||||
requestId?: string;
|
||||
}
|
||||
|
||||
// ── Shared fetch helper ────────────────────────────────────────────────────
|
||||
|
||||
async function lysnraiFetch<T>(
|
||||
path: string,
|
||||
init: RequestInit,
|
||||
opts: LysnraiClientOptions
|
||||
): Promise<T> {
|
||||
const headers: Record<string, string> = {
|
||||
'Content-Type': 'application/json',
|
||||
...(opts.token ? { Authorization: `Bearer ${opts.token}` } : {}),
|
||||
...(opts.requestId ? { 'x-request-id': opts.requestId } : {}),
|
||||
};
|
||||
const res = await fetch(`${config.LYSNRAI_BACKEND_URL}${path}`, {
|
||||
...init,
|
||||
headers: { ...((init.headers as Record<string, string>) ?? {}), ...headers },
|
||||
signal: AbortSignal.timeout(15_000),
|
||||
});
|
||||
if (!res.ok) {
|
||||
const body = await res.text().catch(() => '');
|
||||
throw new Error(`lysnrai-backend ${init.method ?? 'GET'} ${path} → ${res.status}: ${body}`);
|
||||
}
|
||||
return res.json() as Promise<T>;
|
||||
}
|
||||
|
||||
// ── Transcripts ────────────────────────────────────────────────────────────
|
||||
|
||||
export interface TranscriptDoc {
|
||||
id: string;
|
||||
userId: string;
|
||||
productId: string;
|
||||
rawText?: string;
|
||||
cleanedText?: string;
|
||||
wordCount?: number;
|
||||
duration?: number;
|
||||
tokensUsed?: number;
|
||||
context?: string;
|
||||
source?: string;
|
||||
language?: string;
|
||||
extractions?: unknown[];
|
||||
extractionMetadata?: Record<string, unknown>;
|
||||
extractedAt?: string;
|
||||
createdAt: string;
|
||||
}
|
||||
|
||||
export function lysnraiTranscriptsList(
|
||||
params: { limit?: number; offset?: number },
|
||||
opts: LysnraiClientOptions
|
||||
): Promise<{ transcripts: TranscriptDoc[]; 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 lysnraiFetch(`/transcripts${q ? `?${q}` : ''}`, { method: 'GET' }, opts);
|
||||
}
|
||||
|
||||
export function lysnraiTranscriptRunExtraction(
|
||||
transcriptId: string,
|
||||
opts: LysnraiClientOptions
|
||||
): Promise<TranscriptDoc> {
|
||||
return lysnraiFetch(`/transcripts/${transcriptId}/extract`, { method: 'POST' }, opts);
|
||||
}
|
||||
|
||||
// ── STT status ─────────────────────────────────────────────────────────────
|
||||
|
||||
export interface SttStatusResult {
|
||||
productId: string;
|
||||
primary: 'azure' | 'whisper' | 'none';
|
||||
azureConfigured: boolean;
|
||||
whisperConfigured: boolean;
|
||||
azureRegion: string | null;
|
||||
generatedAt: string;
|
||||
}
|
||||
|
||||
export function lysnraiSttGetBackendStatus(opts: LysnraiClientOptions): Promise<SttStatusResult> {
|
||||
return lysnraiFetch('/transcripts/stt-status', { method: 'GET' }, opts);
|
||||
}
|
||||
|
||||
// ── Sessions ───────────────────────────────────────────────────────────────
|
||||
|
||||
export interface SessionDoc {
|
||||
id: string;
|
||||
userId: string;
|
||||
productId: string;
|
||||
title: string;
|
||||
status: 'active' | 'composed' | 'archived';
|
||||
entries: unknown[];
|
||||
composedText: string | null;
|
||||
composedAt: string | null;
|
||||
compositionRating: number | null;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
export function lysnraiSessionsList(
|
||||
params: { limit?: number; offset?: number; status?: string },
|
||||
opts: LysnraiClientOptions
|
||||
): Promise<{ sessions: SessionDoc[]; 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));
|
||||
if (params.status) qs.set('status', params.status);
|
||||
const q = qs.toString();
|
||||
return lysnraiFetch(`/sessions${q ? `?${q}` : ''}`, { method: 'GET' }, opts);
|
||||
}
|
||||
|
||||
// ── Organisations ──────────────────────────────────────────────────────────
|
||||
|
||||
export interface OrgDoc {
|
||||
id: string;
|
||||
name: string;
|
||||
ownerId: string;
|
||||
productId: string;
|
||||
createdAt: string;
|
||||
}
|
||||
|
||||
export function lysnraiOrgsList(
|
||||
params: { limit?: number; offset?: number },
|
||||
opts: LysnraiClientOptions
|
||||
): Promise<{ organizations: OrgDoc[]; 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 lysnraiFetch(`/organizations${q ? `?${q}` : ''}`, { method: 'GET' }, opts);
|
||||
}
|
||||
|
||||
// ── API tokens ─────────────────────────────────────────────────────────────
|
||||
|
||||
export interface ApiTokenDoc {
|
||||
id: string;
|
||||
userId: string;
|
||||
productId: string;
|
||||
name: string;
|
||||
lastUsedAt: string | null;
|
||||
createdAt: string;
|
||||
}
|
||||
|
||||
export function lysnraiApiTokensList(
|
||||
params: { limit?: number; offset?: number },
|
||||
opts: LysnraiClientOptions
|
||||
): Promise<{ tokens: ApiTokenDoc[]; 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 lysnraiFetch(`/api-tokens${q ? `?${q}` : ''}`, { method: 'GET' }, opts);
|
||||
}
|
||||
|
||||
export function lysnraiApiTokenRotate(
|
||||
tokenId: string,
|
||||
opts: LysnraiClientOptions
|
||||
): Promise<{ token: string; id: string }> {
|
||||
return lysnraiFetch(`/api-tokens/${tokenId}/rotate`, { method: 'POST' }, opts);
|
||||
}
|
||||
221
services/mcp-server/src/lib/mindlyst-client.ts
Normal file
221
services/mcp-server/src/lib/mindlyst-client.ts
Normal file
@ -0,0 +1,221 @@
|
||||
/**
|
||||
* MindLyst backend client — typed HTTP wrappers for the mindlyst-backend (port 4014).
|
||||
*
|
||||
* Auth: Bearer token from the caller's JWT (same JWT_SECRET as platform-service).
|
||||
* Admin callers may pass x-admin-user-id to operate on behalf of any user.
|
||||
*/
|
||||
|
||||
import { config } from './config.js';
|
||||
|
||||
export interface MindlystClientOptions {
|
||||
token?: string;
|
||||
requestId?: string;
|
||||
/** When set (admin only) — overrides the JWT sub used as userId in backend routes */
|
||||
adminUserId?: string;
|
||||
}
|
||||
|
||||
// ── Shared fetch helper ────────────────────────────────────────────────────
|
||||
|
||||
async function mindlystFetch<T>(
|
||||
path: string,
|
||||
init: RequestInit,
|
||||
opts: MindlystClientOptions
|
||||
): Promise<T> {
|
||||
const headers: Record<string, string> = {
|
||||
'Content-Type': 'application/json',
|
||||
...(opts.token ? { Authorization: `Bearer ${opts.token}` } : {}),
|
||||
...(opts.requestId ? { 'x-request-id': opts.requestId } : {}),
|
||||
...(opts.adminUserId ? { 'x-admin-user-id': opts.adminUserId } : {}),
|
||||
};
|
||||
const res = await fetch(`${config.MINDLYST_BACKEND_URL}${path}`, {
|
||||
...init,
|
||||
headers: { ...((init.headers as Record<string, string>) ?? {}), ...headers },
|
||||
signal: AbortSignal.timeout(15_000),
|
||||
});
|
||||
if (!res.ok) {
|
||||
const body = await res.text().catch(() => '');
|
||||
throw new Error(`mindlyst-backend ${init.method ?? 'GET'} ${path} → ${res.status}: ${body}`);
|
||||
}
|
||||
return res.json() as Promise<T>;
|
||||
}
|
||||
|
||||
// ── Memory items ───────────────────────────────────────────────────────────
|
||||
|
||||
export interface MemoryItemDoc {
|
||||
id: string;
|
||||
productId: string;
|
||||
userId: string;
|
||||
sourceType: string;
|
||||
captureSurface: string;
|
||||
rawContent: string;
|
||||
triageResult: {
|
||||
contentType: string;
|
||||
summary?: string;
|
||||
urgencyScore: number;
|
||||
emotionScore: number;
|
||||
confidenceScore: number;
|
||||
suggestedBrainId: string;
|
||||
entities: string[];
|
||||
suggestedActions: string[];
|
||||
};
|
||||
brainIds: string[];
|
||||
actedOn: boolean;
|
||||
actedOnAt: string | null;
|
||||
nudgeCount: number;
|
||||
reminderAt?: string;
|
||||
isSensitive: boolean;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
export function mindlystMemoryList(
|
||||
params: {
|
||||
brainId?: string;
|
||||
filter?: 'forgotten' | 'completed_today';
|
||||
limit?: number;
|
||||
offset?: number;
|
||||
productId?: string;
|
||||
},
|
||||
opts: MindlystClientOptions
|
||||
): Promise<{ items: MemoryItemDoc[]; limit: number; offset: number }> {
|
||||
const qs = new URLSearchParams();
|
||||
if (params.brainId) qs.set('brainId', params.brainId);
|
||||
if (params.filter) qs.set('filter', params.filter);
|
||||
if (params.limit !== undefined) qs.set('limit', String(params.limit));
|
||||
if (params.offset !== undefined) qs.set('offset', String(params.offset));
|
||||
if (params.productId) qs.set('productId', params.productId);
|
||||
const q = qs.toString();
|
||||
return mindlystFetch(`/memory-items${q ? `?${q}` : ''}`, { method: 'GET' }, opts);
|
||||
}
|
||||
|
||||
export function mindlystMemoryRetriage(
|
||||
itemId: string,
|
||||
opts: MindlystClientOptions
|
||||
): Promise<MemoryItemDoc> {
|
||||
return mindlystFetch(`/memory-items/${itemId}/retriage`, { method: 'POST' }, opts);
|
||||
}
|
||||
|
||||
export function mindlystMemoryPatch(
|
||||
itemId: string,
|
||||
action: 'mark_done' | 'mark_undone' | 'increment_nudge' | 'set_reminder',
|
||||
reminderAt: string | undefined,
|
||||
opts: MindlystClientOptions
|
||||
): Promise<MemoryItemDoc> {
|
||||
return mindlystFetch(
|
||||
`/memory-items/${itemId}`,
|
||||
{ method: 'PATCH', body: JSON.stringify({ action, ...(reminderAt ? { reminderAt } : {}) }) },
|
||||
opts
|
||||
);
|
||||
}
|
||||
|
||||
export function mindlystMemoryReassign(
|
||||
itemId: string,
|
||||
newBrainId: string,
|
||||
opts: MindlystClientOptions
|
||||
): Promise<MemoryItemDoc> {
|
||||
return mindlystFetch(
|
||||
`/memory-items/${itemId}/reassign`,
|
||||
{ method: 'PUT', body: JSON.stringify({ newBrainId }) },
|
||||
opts
|
||||
);
|
||||
}
|
||||
|
||||
// ── Brains ─────────────────────────────────────────────────────────────────
|
||||
|
||||
export interface BrainDoc {
|
||||
id: string;
|
||||
userId: string;
|
||||
productId: string;
|
||||
name: string;
|
||||
rolePrompt?: string;
|
||||
tone?: string;
|
||||
colorFrom?: string;
|
||||
colorTo?: string;
|
||||
createdAt: string;
|
||||
updatedAt: string | null;
|
||||
}
|
||||
|
||||
export function mindlystBrainsList(
|
||||
params: { limit?: number; offset?: number },
|
||||
opts: MindlystClientOptions
|
||||
): Promise<{ items: BrainDoc[]; total: number; limit: number; offset: 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 mindlystFetch(`/brains${q ? `?${q}` : ''}`, { method: 'GET' }, opts);
|
||||
}
|
||||
|
||||
// ── Daily briefs ───────────────────────────────────────────────────────────
|
||||
|
||||
export interface DailyBriefDoc {
|
||||
id: string;
|
||||
userId: string;
|
||||
productId: string;
|
||||
date: string;
|
||||
greeting?: string;
|
||||
priorityItems: unknown[];
|
||||
brainSummaries: Record<string, string>;
|
||||
streakMessage?: string;
|
||||
motivationalQuote?: string;
|
||||
createdAt: string;
|
||||
}
|
||||
|
||||
export function mindlystBriefsGetToday(opts: MindlystClientOptions): Promise<DailyBriefDoc> {
|
||||
return mindlystFetch('/daily-briefs/today', { method: 'GET' }, opts);
|
||||
}
|
||||
|
||||
export function mindlystBriefsList(
|
||||
params: { limit?: number; offset?: number },
|
||||
opts: MindlystClientOptions
|
||||
): Promise<{ items: DailyBriefDoc[]; 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 mindlystFetch(`/daily-briefs${q ? `?${q}` : ''}`, { method: 'GET' }, opts);
|
||||
}
|
||||
|
||||
// ── Streaks ────────────────────────────────────────────────────────────────
|
||||
|
||||
export interface StreakDoc {
|
||||
id: string;
|
||||
userId: string;
|
||||
productId: string;
|
||||
currentStreak: number;
|
||||
longestStreak: number;
|
||||
lastActiveDate: string;
|
||||
streakFreezeAvailable: boolean;
|
||||
totalActiveDays: number;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
export function mindlystStreaksGet(opts: MindlystClientOptions): Promise<StreakDoc> {
|
||||
return mindlystFetch('/streaks', { method: 'GET' }, opts);
|
||||
}
|
||||
|
||||
// ── Reflections ────────────────────────────────────────────────────────────
|
||||
|
||||
export interface ReflectionDoc {
|
||||
id: string;
|
||||
userId: string;
|
||||
productId: string;
|
||||
weekStartDate: string;
|
||||
themes: string[];
|
||||
highlights: string[];
|
||||
challenges: string[];
|
||||
summary?: string;
|
||||
createdAt: string;
|
||||
}
|
||||
|
||||
export function mindlystReflectionsList(
|
||||
params: { limit?: number; offset?: number },
|
||||
opts: MindlystClientOptions
|
||||
): Promise<{ items: ReflectionDoc[]; 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 mindlystFetch(`/reflections${q ? `?${q}` : ''}`, { method: 'GET' }, opts);
|
||||
}
|
||||
150
services/mcp-server/src/modules/jarvis/jarvis-tools.ts
Normal file
150
services/mcp-server/src/modules/jarvis/jarvis-tools.ts
Normal file
@ -0,0 +1,150 @@
|
||||
/**
|
||||
* JarvisJr MCP tools — jarvis.agents.*, jarvis.sessions.*, jarvis.memory.*
|
||||
*
|
||||
* Backed by: jarvisjr-backend (port 4012).
|
||||
* All tools require admin role.
|
||||
*/
|
||||
|
||||
import { z } from 'zod';
|
||||
import { registerTool } from '../tools/registry.js';
|
||||
import { config } from '../../lib/config.js';
|
||||
import {
|
||||
jarvisAgentsList,
|
||||
jarvisAgentDuplicate,
|
||||
jarvisSessionsList,
|
||||
jarvisSessionsGetStats,
|
||||
jarvisMemoryList,
|
||||
jarvisMemoryPrune,
|
||||
jarvisMemoryGetContext,
|
||||
} from '../../lib/jarvis-client.js';
|
||||
import type { McpToolRequest } from '../tools/types.js';
|
||||
|
||||
const tokenOf = (req: McpToolRequest) => req.headers.authorization?.slice(7);
|
||||
|
||||
// ── jarvis.agents.list ────────────────────────────────────────────────────
|
||||
|
||||
registerTool({
|
||||
name: 'jarvis.agents.list',
|
||||
description:
|
||||
'List coaching agents for the authenticated user (name, role, coachingFramework, totalSessions). 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 jarvisAgentsList(args, { token: tokenOf(req), requestId: req.id });
|
||||
},
|
||||
});
|
||||
|
||||
// ── jarvis.agents.duplicate ───────────────────────────────────────────────
|
||||
|
||||
registerTool({
|
||||
name: 'jarvis.agents.duplicate',
|
||||
description:
|
||||
'Duplicate an existing agent (creates a copy with " (Copy)" suffix and zero session count). Useful for iterating on agent prompts. Requires admin role.',
|
||||
requiredRole: 'admin',
|
||||
inputSchema: z.object({
|
||||
agentId: z.string().min(1).describe('Agent ID to duplicate'),
|
||||
}),
|
||||
async execute(args, req) {
|
||||
return jarvisAgentDuplicate(args.agentId, { token: tokenOf(req), requestId: req.id });
|
||||
},
|
||||
});
|
||||
|
||||
// ── jarvis.sessions.list ──────────────────────────────────────────────────
|
||||
|
||||
registerTool({
|
||||
name: 'jarvis.sessions.list',
|
||||
description:
|
||||
'List coaching sessions for the authenticated user. Filter by agentId. Requires admin role.',
|
||||
requiredRole: 'admin',
|
||||
inputSchema: z.object({
|
||||
agentId: z.string().optional().describe('Filter to a specific agent ID'),
|
||||
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 jarvisSessionsList(args, { token: tokenOf(req), requestId: req.id });
|
||||
},
|
||||
});
|
||||
|
||||
// ── jarvis.sessions.stats ─────────────────────────────────────────────────
|
||||
|
||||
registerTool({
|
||||
name: 'jarvis.sessions.stats',
|
||||
description:
|
||||
'Get coaching session statistics: total sessions, total duration, current streak, per-agent breakdown. Requires admin role.',
|
||||
requiredRole: 'admin',
|
||||
inputSchema: z.object({}),
|
||||
async execute(_args, req) {
|
||||
return jarvisSessionsGetStats({ token: tokenOf(req), requestId: req.id });
|
||||
},
|
||||
});
|
||||
|
||||
// ── jarvis.memory.list ────────────────────────────────────────────────────
|
||||
|
||||
registerTool({
|
||||
name: 'jarvis.memory.list',
|
||||
description:
|
||||
'List persistent memories for a coaching agent. Filter by type (skill_note, preference, goal, context, exercise) or minimum importance score. Requires admin role.',
|
||||
requiredRole: 'admin',
|
||||
inputSchema: z.object({
|
||||
agentId: z.string().min(1).describe('Agent ID to query memories for'),
|
||||
type: z
|
||||
.enum(['skill_note', 'preference', 'goal', 'context', 'exercise'])
|
||||
.optional()
|
||||
.describe('Filter by memory type'),
|
||||
minImportance: z.coerce
|
||||
.number()
|
||||
.min(0)
|
||||
.max(1)
|
||||
.optional()
|
||||
.describe('Minimum importance score (0.0–1.0)'),
|
||||
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) {
|
||||
const { agentId, ...params } = args;
|
||||
return jarvisMemoryList(agentId, params, { token: tokenOf(req), requestId: req.id });
|
||||
},
|
||||
});
|
||||
|
||||
// ── jarvis.memory.prune ───────────────────────────────────────────────────
|
||||
|
||||
registerTool({
|
||||
name: 'jarvis.memory.prune',
|
||||
description:
|
||||
'Prune expired memories for a coaching agent (removes entries where expiresAt < now). Returns count of pruned entries. Requires admin role.',
|
||||
requiredRole: 'admin',
|
||||
inputSchema: z.object({
|
||||
agentId: z.string().min(1).describe('Agent ID to prune memories for'),
|
||||
}),
|
||||
async execute(args, req) {
|
||||
return jarvisMemoryPrune(args.agentId, { token: tokenOf(req), requestId: req.id });
|
||||
},
|
||||
});
|
||||
|
||||
// ── jarvis.memory.getContext ──────────────────────────────────────────────
|
||||
|
||||
registerTool({
|
||||
name: 'jarvis.memory.getContext',
|
||||
description:
|
||||
'Retrieve the top-N memories for a coaching agent to use as session context (sorted by importance). Requires admin role.',
|
||||
requiredRole: 'admin',
|
||||
inputSchema: z.object({
|
||||
agentId: z.string().min(1).describe('Agent ID'),
|
||||
limit: z.coerce
|
||||
.number()
|
||||
.min(1)
|
||||
.max(50)
|
||||
.default(20)
|
||||
.describe('Number of context memories to return'),
|
||||
}),
|
||||
async execute(args, req) {
|
||||
return jarvisMemoryGetContext(args.agentId, args.limit, {
|
||||
token: tokenOf(req),
|
||||
requestId: req.id,
|
||||
});
|
||||
},
|
||||
});
|
||||
133
services/mcp-server/src/modules/lysnrai/lysnrai-tools.ts
Normal file
133
services/mcp-server/src/modules/lysnrai/lysnrai-tools.ts
Normal file
@ -0,0 +1,133 @@
|
||||
/**
|
||||
* 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,
|
||||
lysnraiTranscriptRunExtraction,
|
||||
lysnraiSttGetBackendStatus,
|
||||
lysnraiSessionsList,
|
||||
lysnraiOrgsList,
|
||||
lysnraiApiTokensList,
|
||||
lysnraiApiTokenRotate,
|
||||
} from '../../lib/lysnrai-client.js';
|
||||
import type { McpToolRequest } from '../tools/types.js';
|
||||
|
||||
const tokenOf = (req: McpToolRequest) => req.headers.authorization?.slice(7);
|
||||
|
||||
// ── 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.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.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.apiTokens.rotate ──────────────────────────────────────────────
|
||||
|
||||
registerTool({
|
||||
name: 'lysnrai.apiTokens.rotate',
|
||||
description:
|
||||
'Rotate an API token — invalidates the current secret and issues a new one. Returns the new plaintext token (only visible once). Requires admin role.',
|
||||
requiredRole: 'admin',
|
||||
inputSchema: z.object({
|
||||
tokenId: z.string().min(1).describe('API token ID to rotate'),
|
||||
}),
|
||||
async execute(args, req) {
|
||||
return lysnraiApiTokenRotate(args.tokenId, { token: tokenOf(req), requestId: req.id });
|
||||
},
|
||||
});
|
||||
201
services/mcp-server/src/modules/mindlyst/mindlyst-tools.ts
Normal file
201
services/mcp-server/src/modules/mindlyst/mindlyst-tools.ts
Normal file
@ -0,0 +1,201 @@
|
||||
/**
|
||||
* MindLyst MCP tools — mindlyst.memory.*, mindlyst.brains.*, mindlyst.briefs.*,
|
||||
* mindlyst.streaks.*, mindlyst.reflections.*, mindlyst.extractions.*
|
||||
*
|
||||
* Backed by: mindlyst-backend (port 4014) + extraction-service (port 4005).
|
||||
* All tools require admin role.
|
||||
*/
|
||||
|
||||
import { z } from 'zod';
|
||||
import { registerTool } from '../tools/registry.js';
|
||||
import { config } from '../../lib/config.js';
|
||||
import {
|
||||
mindlystMemoryList,
|
||||
mindlystMemoryRetriage,
|
||||
mindlystMemoryPatch,
|
||||
mindlystMemoryReassign,
|
||||
mindlystBrainsList,
|
||||
mindlystBriefsGetToday,
|
||||
mindlystBriefsList,
|
||||
mindlystStreaksGet,
|
||||
mindlystReflectionsList,
|
||||
} from '../../lib/mindlyst-client.js';
|
||||
import { extractionRun } from '../../lib/extraction-client.js';
|
||||
import type { McpToolRequest } from '../tools/types.js';
|
||||
|
||||
const tokenOf = (req: McpToolRequest) => req.headers.authorization?.slice(7);
|
||||
|
||||
// ── mindlyst.memory.list ──────────────────────────────────────────────────
|
||||
|
||||
registerTool({
|
||||
name: 'mindlyst.memory.list',
|
||||
description:
|
||||
'List MindLyst memory items for the authenticated user. Filter by brain, status, or use free-text. Requires admin role.',
|
||||
requiredRole: 'admin',
|
||||
inputSchema: z.object({
|
||||
brainId: z.string().optional().describe('Filter to a specific Brain ID (e.g. "work", "home")'),
|
||||
filter: z
|
||||
.enum(['forgotten', 'completed_today'])
|
||||
.optional()
|
||||
.describe('forgotten = actedOn=false, completed_today = actedOn=true today'),
|
||||
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 mindlystMemoryList(args, { token: tokenOf(req), requestId: req.id });
|
||||
},
|
||||
});
|
||||
|
||||
// ── mindlyst.memory.retriage ──────────────────────────────────────────────
|
||||
|
||||
registerTool({
|
||||
name: 'mindlyst.memory.retriage',
|
||||
description:
|
||||
'Re-run extraction on a single memory item to refresh its triage result (urgencyScore, suggestedBrainId, entities). Best-effort: returns current item if extraction-service is unavailable. Requires admin role.',
|
||||
requiredRole: 'admin',
|
||||
inputSchema: z.object({
|
||||
itemId: z.string().min(1).describe('Memory item ID'),
|
||||
}),
|
||||
async execute(args, req) {
|
||||
return mindlystMemoryRetriage(args.itemId, { token: tokenOf(req), requestId: req.id });
|
||||
},
|
||||
});
|
||||
|
||||
// ── mindlyst.memory.patch ─────────────────────────────────────────────────
|
||||
|
||||
registerTool({
|
||||
name: 'mindlyst.memory.patch',
|
||||
description:
|
||||
'Update a memory item status: mark_done, mark_undone, increment_nudge, or set_reminder. Requires admin role.',
|
||||
requiredRole: 'admin',
|
||||
inputSchema: z.object({
|
||||
itemId: z.string().min(1).describe('Memory item ID'),
|
||||
action: z.enum(['mark_done', 'mark_undone', 'increment_nudge', 'set_reminder']),
|
||||
reminderAt: z
|
||||
.string()
|
||||
.datetime()
|
||||
.optional()
|
||||
.describe('ISO 8601 datetime — required when action is set_reminder'),
|
||||
}),
|
||||
async execute(args, req) {
|
||||
return mindlystMemoryPatch(args.itemId, args.action, args.reminderAt, {
|
||||
token: tokenOf(req),
|
||||
requestId: req.id,
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
// ── mindlyst.memory.reassign ──────────────────────────────────────────────
|
||||
|
||||
registerTool({
|
||||
name: 'mindlyst.memory.reassign',
|
||||
description:
|
||||
'Reassign a memory item to a different Brain. Records a userCorrection for training. Requires admin role.',
|
||||
requiredRole: 'admin',
|
||||
inputSchema: z.object({
|
||||
itemId: z.string().min(1).describe('Memory item ID'),
|
||||
newBrainId: z.string().min(1).describe('Target Brain ID'),
|
||||
}),
|
||||
async execute(args, req) {
|
||||
return mindlystMemoryReassign(args.itemId, args.newBrainId, {
|
||||
token: tokenOf(req),
|
||||
requestId: req.id,
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
// ── mindlyst.brains.list ──────────────────────────────────────────────────
|
||||
|
||||
registerTool({
|
||||
name: 'mindlyst.brains.list',
|
||||
description:
|
||||
'List all Brains for the authenticated user (role-based life OS categories). 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 mindlystBrainsList(args, { token: tokenOf(req), requestId: req.id });
|
||||
},
|
||||
});
|
||||
|
||||
// ── mindlyst.briefs.getToday ──────────────────────────────────────────────
|
||||
|
||||
registerTool({
|
||||
name: 'mindlyst.briefs.getToday',
|
||||
description:
|
||||
"Get today's Daily Brief for the authenticated user. Returns 404 error if none generated yet. Requires admin role.",
|
||||
requiredRole: 'admin',
|
||||
inputSchema: z.object({}),
|
||||
async execute(_args, req) {
|
||||
return mindlystBriefsGetToday({ token: tokenOf(req), requestId: req.id });
|
||||
},
|
||||
});
|
||||
|
||||
// ── mindlyst.briefs.list ──────────────────────────────────────────────────
|
||||
|
||||
registerTool({
|
||||
name: 'mindlyst.briefs.list',
|
||||
description: 'List past Daily Briefs 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 mindlystBriefsList(args, { token: tokenOf(req), requestId: req.id });
|
||||
},
|
||||
});
|
||||
|
||||
// ── mindlyst.streaks.get ──────────────────────────────────────────────────
|
||||
|
||||
registerTool({
|
||||
name: 'mindlyst.streaks.get',
|
||||
description:
|
||||
'Get the current engagement streak for the authenticated user (currentStreak, longestStreak, lastActiveDate). Requires admin role.',
|
||||
requiredRole: 'admin',
|
||||
inputSchema: z.object({}),
|
||||
async execute(_args, req) {
|
||||
return mindlystStreaksGet({ token: tokenOf(req), requestId: req.id });
|
||||
},
|
||||
});
|
||||
|
||||
// ── mindlyst.reflections.list ─────────────────────────────────────────────
|
||||
|
||||
registerTool({
|
||||
name: 'mindlyst.reflections.list',
|
||||
description: 'List reflection journal entries 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 mindlystReflectionsList(args, { token: tokenOf(req), requestId: req.id });
|
||||
},
|
||||
});
|
||||
|
||||
// ── mindlyst.extractions.run ──────────────────────────────────────────────
|
||||
|
||||
registerTool({
|
||||
name: 'mindlyst.extractions.run',
|
||||
description: [
|
||||
'Run text through the MindLyst extraction pipeline with one of the three task IDs:',
|
||||
' • triage — extract brain signals, entities, actions (used for new memory items)',
|
||||
' • memory-insight — summarise a set of memory items into brain-level insights',
|
||||
' • reflection-enrichment — extract themes and highlights from journal text',
|
||||
'Requires admin role.',
|
||||
].join('\n'),
|
||||
requiredRole: 'admin',
|
||||
inputSchema: z.object({
|
||||
text: z.string().min(1).describe('Text to extract from'),
|
||||
taskId: z
|
||||
.enum(['triage', 'memory-insight', 'reflection-enrichment'])
|
||||
.default('triage')
|
||||
.describe('MindLyst-specific extraction task ID'),
|
||||
}),
|
||||
async execute(args, req) {
|
||||
return extractionRun({ text: args.text, taskId: args.taskId }, { requestId: req.id });
|
||||
},
|
||||
});
|
||||
@ -6,6 +6,9 @@
|
||||
* platform.diagnostics.* — manage debug sessions, read logs/traces, cancel
|
||||
* extraction.* — run extraction, list models, cache stats, sidecar health
|
||||
* support.* — compound tools (createDebugPack, runIncidentPipeline)
|
||||
* mindlyst.* — memory, brains, briefs, streaks, reflections, extractions
|
||||
* lysnrai.* — transcripts, sessions, orgs, apiTokens, stt
|
||||
* jarvis.* — agents, sessions, memory (JarvisJr coaching platform)
|
||||
*
|
||||
* Auth: JWT Bearer tokens issued by platform-service (same JWT_SECRET).
|
||||
* Role gating: viewer / admin / super_admin per tool.
|
||||
@ -23,12 +26,15 @@ import './modules/platform/diagnostics-tools.js';
|
||||
import './modules/extraction/extraction-tools.js';
|
||||
import './modules/support/debug-pack.js';
|
||||
import './modules/a2a/pipeline-tool.js';
|
||||
import './modules/mindlyst/mindlyst-tools.js';
|
||||
import './modules/lysnrai/lysnrai-tools.js';
|
||||
import './modules/jarvis/jarvis-tools.js';
|
||||
|
||||
const app = await createServiceApp({
|
||||
name: 'mcp-server',
|
||||
version: '0.1.0',
|
||||
description:
|
||||
'ByteLyst MCP Server — platform.telemetry.*, platform.diagnostics.*, extraction.*, support.*',
|
||||
'ByteLyst MCP Server — platform.*, extraction.*, support.*, mindlyst.*, lysnrai.*, jarvis.*',
|
||||
corsOrigin: config.CORS_ORIGIN,
|
||||
logLevel: config.LOG_LEVEL,
|
||||
});
|
||||
|
||||
Loading…
Reference in New Issue
Block a user