@cachly-dev/mcp-server 0.10.82 → 0.10.83

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,6 @@
1
+ import type { Redis } from 'ioredis';
2
+ type GetConnection = (instanceId: string) => Promise<Redis>;
3
+ type ApiFetch = <T>(path: string, options?: RequestInit) => Promise<T>;
4
+ export declare const VIZ_TOOL_NAMES: Set<string>;
5
+ export declare function handleVizTool(name: string, args: Record<string, unknown>, getConnection: GetConnection, _apiFetch: ApiFetch): Promise<string | null>;
6
+ export {};
@@ -0,0 +1,148 @@
1
+ import { safeJsonParse } from '../utils.js';
2
+ export const VIZ_TOOL_NAMES = new Set(['brain_graph']);
3
+ // Stable color groups so the 3D frontend can theme node kinds consistently.
4
+ const NODE_GROUP = {
5
+ concept: 1, person: 2, file: 3, service: 4,
6
+ };
7
+ function nodeKind(id) {
8
+ if (id.startsWith('person:'))
9
+ return 'person';
10
+ if (id.startsWith('file:'))
11
+ return 'file';
12
+ if (id.startsWith('service:'))
13
+ return 'service';
14
+ return 'concept';
15
+ }
16
+ function nodeLabel(n, kind) {
17
+ if (kind === 'person')
18
+ return n.handle ?? n.id.replace(/^person:/, '');
19
+ if (kind === 'file')
20
+ return (n.path ?? n.id.replace(/^file:/, '')).split('/').slice(-1)[0] ?? n.id;
21
+ if (kind === 'service')
22
+ return n.name ?? n.id.replace(/^service:/, '');
23
+ return n.id;
24
+ }
25
+ async function scanAll(redis, match, limit) {
26
+ const keys = [];
27
+ const stream = redis.scanStream({ match, count: 200 });
28
+ await new Promise((resolve, reject) => {
29
+ stream.on('data', (batch) => {
30
+ for (const k of batch) {
31
+ if (keys.length < limit)
32
+ keys.push(k);
33
+ }
34
+ if (keys.length >= limit)
35
+ stream.destroy();
36
+ });
37
+ stream.on('end', resolve);
38
+ stream.on('close', resolve);
39
+ stream.on('error', reject);
40
+ });
41
+ return keys;
42
+ }
43
+ export async function handleVizTool(name, args, getConnection, _apiFetch) {
44
+ if (name !== 'brain_graph')
45
+ return null;
46
+ const { instance_id, max_nodes = 400, domain = '', min_confidence = 0, format = 'json', } = args;
47
+ const redis = await getConnection(instance_id);
48
+ const nodeCap = Math.max(1, Math.min(2000, Number(max_nodes) || 400));
49
+ const minConf = Math.max(0, Math.min(1, Number(min_confidence) || 0));
50
+ const domainFilter = String(domain || '').toLowerCase().trim();
51
+ // ── Nodes ────────────────────────────────────────────────────────────────
52
+ const nodeKeys = await scanAll(redis, 'cachly:ckg:node:*', nodeCap);
53
+ const nodes = [];
54
+ const present = new Set();
55
+ for (const key of nodeKeys) {
56
+ const raw = await redis.get(key);
57
+ const stored = safeJsonParse(raw, null);
58
+ if (!stored || !stored.id)
59
+ continue;
60
+ const kind = nodeKind(stored.id);
61
+ const ndomain = String(stored.domain ?? '').toLowerCase();
62
+ if (domainFilter && !ndomain.includes(domainFilter) && !stored.id.toLowerCase().includes(domainFilter))
63
+ continue;
64
+ const count = Number(stored.count ?? 1) || 1;
65
+ nodes.push({
66
+ id: stored.id,
67
+ name: nodeLabel(stored, kind),
68
+ type: kind,
69
+ group: NODE_GROUP[kind] ?? 0,
70
+ domain: stored.domain ?? '',
71
+ val: Math.max(1, Math.round(Math.sqrt(count) * 3)),
72
+ count,
73
+ });
74
+ present.add(stored.id);
75
+ }
76
+ // ── Edges ──────────────────────────────────────────────────────────────────
77
+ // Edge keys: cachly:ckg:edge:{from}:{edgeType}:{to}. We read the stored CKGEdge
78
+ // (not the key) so from/to/confidence come straight from the value — robust to
79
+ // ids that themselves contain ':' (e.g. "docker:layer-cache").
80
+ const edgeKeys = await scanAll(redis, 'cachly:ckg:edge:*', nodeCap * 8);
81
+ const links = [];
82
+ let edgesSkippedConf = 0;
83
+ let edgesSkippedDangling = 0;
84
+ for (const key of edgeKeys) {
85
+ const raw = await redis.get(key);
86
+ const edge = safeJsonParse(raw, null);
87
+ if (!edge || !edge.from || !edge.to)
88
+ continue;
89
+ if ((edge.confidence ?? 0) < minConf) {
90
+ edgesSkippedConf++;
91
+ continue;
92
+ }
93
+ // Drop dangling edges (endpoint filtered out) so the 3D graph stays consistent.
94
+ if (!present.has(edge.from) || !present.has(edge.to)) {
95
+ edgesSkippedDangling++;
96
+ continue;
97
+ }
98
+ links.push({
99
+ source: edge.from,
100
+ target: edge.to,
101
+ type: edge.edgeType ?? 'related',
102
+ value: Number((edge.confidence ?? 0).toFixed(3)),
103
+ trials: edge.trials ?? 0,
104
+ });
105
+ }
106
+ const byType = {};
107
+ for (const n of nodes)
108
+ byType[n.type] = (byType[n.type] ?? 0) + 1;
109
+ const graph = {
110
+ schema: 'cachly.brain_graph/v1',
111
+ nodes,
112
+ links,
113
+ meta: {
114
+ instance_id,
115
+ generated_at: new Date().toISOString(),
116
+ node_count: nodes.length,
117
+ link_count: links.length,
118
+ node_types: byType,
119
+ truncated: nodeKeys.length >= nodeCap,
120
+ filters: { domain: domainFilter || null, min_confidence: minConf, max_nodes: nodeCap },
121
+ },
122
+ };
123
+ if (format === 'summary') {
124
+ const lines = [
125
+ `## 🧠 Brain Graph — 3D Viz Export`,
126
+ '',
127
+ `**${nodes.length}** nodes · **${links.length}** links`,
128
+ ...Object.entries(byType).map(([t, c]) => ` • ${t}: ${c}`),
129
+ edgesSkippedConf ? ` • ${edgesSkippedConf} edges below confidence ${minConf}` : '',
130
+ edgesSkippedDangling ? ` • ${edgesSkippedDangling} dangling edges dropped` : '',
131
+ '',
132
+ graph.meta.truncated ? `⚠️ Truncated at ${nodeCap} nodes — raise \`max_nodes\` for the full graph.` : '',
133
+ '',
134
+ `Call \`brain_graph(instance_id="${instance_id}", format="json")\` for the full payload the 3D frontend renders.`,
135
+ ].filter(Boolean);
136
+ return lines.join('\n');
137
+ }
138
+ // Default: emit the renderable payload in a fenced block so the frontend /
139
+ // API can lift it verbatim into react-force-graph-3d.
140
+ return [
141
+ `🧠 **Brain Graph** — ${nodes.length} nodes, ${links.length} links (schema \`cachly.brain_graph/v1\`)`,
142
+ '',
143
+ '```json',
144
+ JSON.stringify(graph, null, 2),
145
+ '```',
146
+ ].join('\n');
147
+ }
148
+ //# sourceMappingURL=viz.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"viz.js","sourceRoot":"","sources":["../../../src/handlers/viz.ts"],"names":[],"mappings":"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"}
package/dist/src/index.js CHANGED
@@ -65,7 +65,7 @@ import { Redis } from 'ioredis';
65
65
  let API_URL = process.env.CACHLY_API_URL ?? 'https://api.cachly.dev';
66
66
  let JWT = process.env.CACHLY_JWT ?? '';
67
67
  const _EMBED_MODEL = process.env.CACHLY_EMBED_MODEL ?? '';
68
- const CURRENT_VERSION = '0.10.82';
68
+ const CURRENT_VERSION = '0.10.83';
69
69
  // Max time to wait for a freshly-provisioned instance to become "running" before
70
70
  // giving up. Free-tier provisioning in high-latency regions can take 45–90s, so the
71
71
  // old hard-coded 25s caused spurious "instance_not_reachable" on first run.
@@ -610,6 +610,7 @@ import { handleAdvancedTool } from './handlers/advanced.js';
610
610
  import { handleSyndicateTool } from './handlers/syndicate.js';
611
611
  import { handleFedbrainTool, _lastBrainFromGitCounts } from './handlers/fedbrain.js';
612
612
  import { handleShareTool } from './handlers/share.js';
613
+ import { handleVizTool } from './handlers/viz.js';
613
614
  // ── Tools (imported from tools.ts) ─────────────────────────────────────────
614
615
  import { TOOLS } from './tools.js';
615
616
  // ── Handlers ──────────────────────────────────────────────────────────────────
@@ -882,6 +883,9 @@ async function handleTool(name, args) {
882
883
  const shareResult = await handleShareTool(name, args, getConnection, apiFetch);
883
884
  if (shareResult !== null)
884
885
  return shareResult;
886
+ const vizResult = await handleVizTool(name, args, getConnection, apiFetch);
887
+ if (vizResult !== null)
888
+ return vizResult;
885
889
  const fedbrainResult = await handleFedbrainTool(name, args, getConnection, apiFetch);
886
890
  if (fedbrainResult !== null) {
887
891
  // brain_from_git and brain_predict_failures are in fedbrain handler.