● Zion Boggan
repos/Zion Pm/lib/memory.ts
zionboggan.com ↗
49 lines · typescript
History for this file →
1
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
2
import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js';
3
 
4
const RAG_URL = process.env.ZPM_RAG_SSE || 'http://REDACTED-IP:9876/sse';
5
const CBM_URL = process.env.ZPM_CBM_SSE || 'http://REDACTED-IP:9877/sse';
6
 
7
function withTimeout<T>(p: Promise<T>, ms: number, label: string): Promise<T> {
8
  return Promise.race([
9
    p,
10
    new Promise<T>((_, rej) => setTimeout(() => rej(new Error(`${label} timeout ${ms}ms`)), ms)),
11
  ]);
12
}
13
 
14
async function mcpCall(url: string, name: string, args: Record<string, unknown>, timeoutMs = 60_000): Promise<string> {
15
  const c = new Client({ name: 'zion-pm', version: '1.0.0' }, { capabilities: {} });
16
  try {
17
    await withTimeout(c.connect(new SSEClientTransport(new URL(url))), 15_000, 'mcp connect');
18
    const r = (await withTimeout(c.callTool({ name, arguments: args }), timeoutMs, 'mcp call')) as { content?: Array<{ text?: string }> };
19
    return (r?.content || []).map((b) => b.text || '').join('\n');
20
  } finally {
21
    try { await c.close(); } catch {  }
22
  }
23
}
24
 
25
export async function queryMemory(query: string, limit = 6): Promise<string> {
26
  try {
27
    return await mcpCall(RAG_URL, 'homelab_context', { query, limit });
28
  } catch (e) {
29
    return `queryMemory unavailable: ${(e as Error).message}`;
30
  }
31
}
32
 
33
const looksLikeError = (s: string) => !s || /not found|not indexed|"error"|validation|required property|must have/i.test(s);
34
 
35
export async function queryGraph(query: string): Promise<string> {
36
  try {
37
    const arch = await mcpCall(CBM_URL, 'get_architecture', { project: query });
38
    if (!looksLikeError(arch)) return arch;
39
  } catch {  }
40
  try {
41
    const s = await mcpCall(CBM_URL, 'search_graph', { query });
42
    if (!looksLikeError(s)) return s;
43
  } catch {  }
44
  try {
45
    const projects = await mcpCall(CBM_URL, 'list_projects', {});
46
    if (!looksLikeError(projects)) return `No single code-graph project matched the query. Indexed projects available:\n${projects}`;
47
  } catch {  }
48
  return 'No matching code-graph project (use queryMemory for prose/docs).';
49
}