learning_ai_common_plat/services/mcp-server/src/lib/jarvis-client.ts
saravanakumardb1 d7aa90b021 feat(mcp-server): 4 MCP tool gaps + 3 new A2A pipelines (Priority 3/6/7)
MCP tool gaps filled (DOMAIN_PRODUCTS.md alignment):
- jarvis.memory.create — POST /jarvis/agents/:agentId/memory (sessionId, type, content, importance, tags, expiresAt)
- jarvis.teams.listMembers — GET /jarvis/teams/:teamId/members (role, status, joinedAt)
- nomgap.fasting.getSession — GET /fasting/sessions/:id (client func already existed, MCP tool was missing)
- peakpulse.weather.getSnapshot — extracts weather field from peakpulseSessionGet response

New A2A pipelines (all registered in server.ts):
- transcript-extraction-pipeline.ts: lysnrai.transcripts.runExtractionPipeline
  - TranscriptCollectorAgent -> ExtractionBatchAgent -> ExtractionReportAgent
  - Queries transcripts missing extractedAt, runs extraction, returns batch report + dryRun support
- sync-conflict-pipeline.ts: chronomind.sync.diagnoseConflicts
  - ConflictDetectorAgent -> SyncStateInspectorAgent -> DiagnosticsSessionAgent -> ConflictReportAgent
  - Queries telemetry for sync_conflict events, classifies pattern, creates diagnostics session on conflict
- route-safety-pipeline.ts: peakpulse.sessions.assessSafety
  - SessionDataAgent -> RouteProfileAgent -> SafetyAnalysisAgent -> SafetyReportAgent
  - Fetches GPS + weather, evaluates UV/wind/altitude/speed risk factors, enriches with extraction entities

Client additions (jarvis-client.ts):
  jarvisMemoryCreate, jarvisTeamsListMembers + JarvisTeamMemberDoc interface

MCP server total: 93 tools across 17 namespaces
2026-03-05 15:18:21 -08:00

288 lines
9.0 KiB
TypeScript

/**
* 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}/api${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 interface AgentCreateInput {
name: string;
role: string;
systemPrompt: string;
voiceId?: string;
coachingFramework?: string;
accentColor?: string;
welcomeMessage?: string;
sessionLength?: number;
difficultyLevel?: 'beginner' | 'intermediate' | 'advanced';
language?: string;
privacyLevel?: 'standard' | 'strict';
}
export function jarvisAgentCreate(
input: AgentCreateInput,
opts: JarvisClientOptions
): Promise<JarvisAgentDoc> {
return jarvisFetch('/jarvis/agents', { method: 'POST', body: JSON.stringify(input) }, opts);
}
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
);
}
// ── Marketplace admin ─────────────────────────────────────────────────────
export function jarvisMarketplaceListPending(
opts: JarvisClientOptions
): Promise<{ listings: unknown[]; total: number }> {
return jarvisFetch('/marketplace/admin/pending', { method: 'GET' }, opts);
}
export function jarvisMarketplaceCertify(
listingId: string,
decision: { decision: 'approved' | 'rejected'; notes?: string },
opts: JarvisClientOptions
): Promise<unknown> {
const action = decision.decision === 'approved' ? 'approve' : 'reject';
return jarvisFetch(
`/marketplace/admin/${listingId}/${action}`,
{ method: 'POST', body: JSON.stringify(decision) },
opts
);
}
export function jarvisMarketplaceGetListing(
listingId: string,
opts: JarvisClientOptions
): Promise<Record<string, unknown>> {
return jarvisFetch(`/marketplace/listings/${listingId}`, { method: 'GET' }, opts);
}
export function jarvisMarketplaceSuspend(
listingId: string,
reason: string,
opts: JarvisClientOptions
): Promise<unknown> {
return jarvisFetch(
`/marketplace/admin/${listingId}/suspend`,
{ method: 'POST', body: JSON.stringify({ reason }) },
opts
);
}
export function jarvisMarketplaceFeature(
listingId: string,
featured: boolean,
opts: JarvisClientOptions
): Promise<unknown> {
return jarvisFetch(
`/marketplace/admin/${listingId}/feature`,
{ method: 'POST', body: JSON.stringify({ featured }) },
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);
}
export function jarvisMemoryCreate(
agentId: string,
input: {
sessionId: string;
type: 'skill_note' | 'preference' | 'goal' | 'context' | 'exercise';
content: string;
importance?: number;
tags?: string[];
expiresAt?: string | null;
},
opts: JarvisClientOptions
): Promise<JarvisMemoryDoc> {
return jarvisFetch(
`/jarvis/agents/${agentId}/memory`,
{ method: 'POST', body: JSON.stringify({ ...input, agentId }) },
opts
);
}
// ── Teams ─────────────────────────────────────────────────────────────────
export interface JarvisTeamMemberDoc {
userId: string;
teamId: string;
role: 'owner' | 'manager' | 'member';
status: 'active' | 'invited' | 'removed';
joinedAt: string;
invitedBy?: string;
}
export function jarvisTeamsListMembers(
teamId: string,
opts: JarvisClientOptions
): Promise<{ members: JarvisTeamMemberDoc[]; total: number }> {
return jarvisFetch(`/jarvis/teams/${teamId}/members`, { method: 'GET' }, opts);
}