@claude-flow/plugin-hyperbolic-reasoning 3.0.0-alpha.1

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,740 @@
1
+ /**
2
+ * Hyperbolic Reasoning MCP Tools
3
+ *
4
+ * MCP tool definitions for hyperbolic geometry operations including:
5
+ * - hyperbolic/embed-hierarchy: Embed hierarchies in Poincare ball
6
+ * - hyperbolic/taxonomic-reason: Taxonomic reasoning and queries
7
+ * - hyperbolic/semantic-search: Hierarchically-aware search
8
+ * - hyperbolic/hierarchy-compare: Compare hierarchical structures
9
+ * - hyperbolic/entailment-graph: Build and query entailment graphs
10
+ */
11
+ import { EmbedHierarchyInputSchema, TaxonomicReasonInputSchema, SemanticSearchInputSchema, HierarchyCompareInputSchema, EntailmentGraphInputSchema, successResult, errorResult, RESOURCE_LIMITS, poincareDistance, } from './types.js';
12
+ import { HyperbolicBridge } from './bridges/hyperbolic-bridge.js';
13
+ import { GnnBridge } from './bridges/gnn-bridge.js';
14
+ // Default logger
15
+ const defaultLogger = {
16
+ debug: (msg, meta) => console.debug(`[hyperbolic-reasoning] ${msg}`, meta),
17
+ info: (msg, meta) => console.info(`[hyperbolic-reasoning] ${msg}`, meta),
18
+ warn: (msg, meta) => console.warn(`[hyperbolic-reasoning] ${msg}`, meta),
19
+ error: (msg, meta) => console.error(`[hyperbolic-reasoning] ${msg}`, meta),
20
+ };
21
+ // Shared bridge instances
22
+ let hyperbolicBridge = null;
23
+ let gnnBridge = null;
24
+ // Stored embeddings and taxonomies
25
+ const embeddingStore = new Map();
26
+ const taxonomyStore = new Map();
27
+ async function getHyperbolicBridge() {
28
+ if (!hyperbolicBridge) {
29
+ hyperbolicBridge = new HyperbolicBridge();
30
+ await hyperbolicBridge.initialize();
31
+ }
32
+ return hyperbolicBridge;
33
+ }
34
+ async function getGnnBridge() {
35
+ if (!gnnBridge) {
36
+ gnnBridge = new GnnBridge();
37
+ await gnnBridge.initialize();
38
+ }
39
+ return gnnBridge;
40
+ }
41
+ // ============================================================================
42
+ // Tool 1: hyperbolic/embed-hierarchy
43
+ // ============================================================================
44
+ async function embedHierarchyHandler(input, context) {
45
+ const logger = context?.logger ?? defaultLogger;
46
+ const startTime = performance.now();
47
+ try {
48
+ const validationResult = EmbedHierarchyInputSchema.safeParse(input);
49
+ if (!validationResult.success) {
50
+ return errorResult(`Invalid input: ${validationResult.error.message}`);
51
+ }
52
+ const data = validationResult.data;
53
+ logger.debug('Embed hierarchy', {
54
+ nodes: data.hierarchy.nodes.length,
55
+ model: data.model,
56
+ dimensions: data.parameters?.dimensions,
57
+ });
58
+ // Validate size
59
+ if (data.hierarchy.nodes.length > RESOURCE_LIMITS.MAX_NODES) {
60
+ return errorResult(`Too many nodes: ${data.hierarchy.nodes.length} > ${RESOURCE_LIMITS.MAX_NODES}`);
61
+ }
62
+ const bridge = await getHyperbolicBridge();
63
+ const result = await bridge.embedHierarchy(data.hierarchy, data.parameters);
64
+ // Store for later use
65
+ const indexId = `embed_${Date.now()}`;
66
+ embeddingStore.set(indexId, result);
67
+ // Create index for search
68
+ bridge.createIndex(indexId, result.dimension, result.curvature);
69
+ for (const [nodeId, point] of result.embeddings) {
70
+ bridge.addToIndex(indexId, nodeId, point);
71
+ }
72
+ const duration = performance.now() - startTime;
73
+ logger.info('Hierarchy embedded', {
74
+ nodes: result.embeddings.size,
75
+ curvature: result.curvature,
76
+ mapScore: result.metrics.mapScore,
77
+ durationMs: duration.toFixed(2),
78
+ });
79
+ // Convert embeddings to serializable format
80
+ const embeddingsList = [];
81
+ for (const [nodeId, point] of result.embeddings) {
82
+ embeddingsList.push({
83
+ id: nodeId,
84
+ coordinates: Array.from(point.coordinates).slice(0, 5), // First 5 dimensions
85
+ depth: bridge.hierarchyDepth(point),
86
+ });
87
+ }
88
+ return successResult({
89
+ indexId,
90
+ model: result.model,
91
+ curvature: result.curvature,
92
+ dimension: result.dimension,
93
+ metrics: result.metrics,
94
+ embeddings: embeddingsList.slice(0, 20), // Sample of embeddings
95
+ totalNodes: result.embeddings.size,
96
+ });
97
+ }
98
+ catch (error) {
99
+ logger.error('Embedding failed', { error: error instanceof Error ? error.message : String(error) });
100
+ return errorResult(error instanceof Error ? error : new Error(String(error)));
101
+ }
102
+ }
103
+ export const embedHierarchyTool = {
104
+ name: 'hyperbolic_embed_hierarchy',
105
+ description: 'Embed hierarchical structure in Poincare ball. Uses hyperbolic geometry for optimal tree representation with logarithmic distortion.',
106
+ category: 'hyperbolic',
107
+ version: '0.1.0',
108
+ tags: ['hyperbolic', 'poincare', 'hierarchy', 'embedding', 'tree'],
109
+ cacheable: true,
110
+ cacheTTL: 300000,
111
+ inputSchema: {
112
+ type: 'object',
113
+ properties: {
114
+ hierarchy: {
115
+ type: 'object',
116
+ properties: {
117
+ nodes: {
118
+ type: 'array',
119
+ items: {
120
+ type: 'object',
121
+ properties: {
122
+ id: { type: 'string' },
123
+ parent: { type: 'string' },
124
+ features: { type: 'object' },
125
+ },
126
+ },
127
+ },
128
+ edges: { type: 'array' },
129
+ },
130
+ },
131
+ model: { type: 'string', enum: ['poincare_ball', 'lorentz', 'klein', 'half_plane'] },
132
+ parameters: {
133
+ type: 'object',
134
+ properties: {
135
+ dimensions: { type: 'number', default: 32 },
136
+ curvature: { type: 'number', default: -1.0 },
137
+ learnCurvature: { type: 'boolean', default: true },
138
+ epochs: { type: 'number', default: 100 },
139
+ learningRate: { type: 'number', default: 0.01 },
140
+ },
141
+ },
142
+ },
143
+ required: ['hierarchy'],
144
+ },
145
+ handler: embedHierarchyHandler,
146
+ };
147
+ // ============================================================================
148
+ // Tool 2: hyperbolic/taxonomic-reason
149
+ // ============================================================================
150
+ async function taxonomicReasonHandler(input, context) {
151
+ const logger = context?.logger ?? defaultLogger;
152
+ const startTime = performance.now();
153
+ try {
154
+ const validationResult = TaxonomicReasonInputSchema.safeParse(input);
155
+ if (!validationResult.success) {
156
+ return errorResult(`Invalid input: ${validationResult.error.message}`);
157
+ }
158
+ const data = validationResult.data;
159
+ logger.debug('Taxonomic reason', { type: data.query.type, subject: data.query.subject });
160
+ // Get stored embedding
161
+ const stored = embeddingStore.get(data.taxonomy);
162
+ if (!stored) {
163
+ return errorResult(`Taxonomy not found: ${data.taxonomy}. Use hyperbolic_embed_hierarchy first.`);
164
+ }
165
+ const subjectEmb = stored.embeddings.get(data.query.subject);
166
+ if (!subjectEmb) {
167
+ return errorResult(`Subject not found in taxonomy: ${data.query.subject}`);
168
+ }
169
+ const bridge = await getHyperbolicBridge();
170
+ let result;
171
+ let confidence = 1.0;
172
+ let explanation = '';
173
+ const steps = [];
174
+ switch (data.query.type) {
175
+ case 'is_a': {
176
+ if (!data.query.object) {
177
+ return errorResult('Object required for is_a query');
178
+ }
179
+ const objectEmb = stored.embeddings.get(data.query.object);
180
+ if (!objectEmb) {
181
+ return errorResult(`Object not found in taxonomy: ${data.query.object}`);
182
+ }
183
+ // Check if subject is descendant of object (object is ancestor)
184
+ const isAncestor = bridge.isAncestor(objectEmb, subjectEmb);
185
+ const distance = bridge.distance(subjectEmb, objectEmb);
186
+ result = isAncestor;
187
+ confidence = Math.exp(-distance / 2);
188
+ explanation = isAncestor
189
+ ? `${data.query.subject} IS-A ${data.query.object} (distance: ${distance.toFixed(3)})`
190
+ : `${data.query.subject} is NOT-A ${data.query.object}`;
191
+ break;
192
+ }
193
+ case 'subsumption': {
194
+ if (!data.query.object) {
195
+ return errorResult('Object required for subsumption query');
196
+ }
197
+ const objectEmb = stored.embeddings.get(data.query.object);
198
+ if (!objectEmb) {
199
+ return errorResult(`Object not found in taxonomy: ${data.query.object}`);
200
+ }
201
+ // Check both directions
202
+ const subjectSubsumesObject = bridge.isAncestor(subjectEmb, objectEmb);
203
+ const objectSubsumesSubject = bridge.isAncestor(objectEmb, subjectEmb);
204
+ if (subjectSubsumesObject) {
205
+ result = 'subject_subsumes_object';
206
+ explanation = `${data.query.subject} subsumes ${data.query.object}`;
207
+ }
208
+ else if (objectSubsumesSubject) {
209
+ result = 'object_subsumes_subject';
210
+ explanation = `${data.query.object} subsumes ${data.query.subject}`;
211
+ }
212
+ else {
213
+ result = 'no_subsumption';
214
+ explanation = `No subsumption relation between ${data.query.subject} and ${data.query.object}`;
215
+ }
216
+ confidence = 0.9;
217
+ break;
218
+ }
219
+ case 'lowest_common_ancestor': {
220
+ if (!data.query.object) {
221
+ return errorResult('Object required for LCA query');
222
+ }
223
+ const objectEmb = stored.embeddings.get(data.query.object);
224
+ if (!objectEmb) {
225
+ return errorResult(`Object not found in taxonomy: ${data.query.object}`);
226
+ }
227
+ // Find LCA by searching for node closest to midpoint
228
+ let bestLca = '';
229
+ let bestScore = Infinity;
230
+ for (const [nodeId, nodeEmb] of stored.embeddings) {
231
+ // Check if this node is ancestor of both
232
+ const isAncOfSubject = bridge.isAncestor(nodeEmb, subjectEmb);
233
+ const isAncOfObject = bridge.isAncestor(nodeEmb, objectEmb);
234
+ if (isAncOfSubject && isAncOfObject) {
235
+ const depth = bridge.hierarchyDepth(nodeEmb);
236
+ // Prefer deepest common ancestor (highest depth value)
237
+ const score = -depth;
238
+ if (score < bestScore) {
239
+ bestScore = score;
240
+ bestLca = nodeId;
241
+ }
242
+ }
243
+ }
244
+ result = bestLca || null;
245
+ confidence = bestLca ? 0.95 : 0;
246
+ explanation = bestLca
247
+ ? `Lowest common ancestor is ${bestLca}`
248
+ : 'No common ancestor found';
249
+ break;
250
+ }
251
+ case 'path': {
252
+ if (!data.query.object) {
253
+ return errorResult('Object required for path query');
254
+ }
255
+ // Find path through ancestors
256
+ const path = [data.query.subject];
257
+ let current = data.query.subject;
258
+ // Find ancestors of subject
259
+ const ancestorsOfSubject = [];
260
+ for (const [nodeId, nodeEmb] of stored.embeddings) {
261
+ if (bridge.isAncestor(nodeEmb, subjectEmb)) {
262
+ ancestorsOfSubject.push(nodeId);
263
+ }
264
+ }
265
+ // Sort by depth (shallowest first)
266
+ ancestorsOfSubject.sort((a, b) => {
267
+ const depthA = bridge.hierarchyDepth(stored.embeddings.get(a));
268
+ const depthB = bridge.hierarchyDepth(stored.embeddings.get(b));
269
+ return depthA - depthB;
270
+ });
271
+ // Check if object is in ancestors
272
+ if (ancestorsOfSubject.includes(data.query.object)) {
273
+ path.push(...ancestorsOfSubject.slice(0, ancestorsOfSubject.indexOf(data.query.object) + 1));
274
+ result = path;
275
+ explanation = `Path from ${data.query.subject} to ${data.query.object}: ${path.join(' -> ')}`;
276
+ }
277
+ else {
278
+ // Find common ancestor and path through it
279
+ result = [];
280
+ explanation = `No direct path from ${data.query.subject} to ${data.query.object}`;
281
+ }
282
+ confidence = 0.9;
283
+ break;
284
+ }
285
+ case 'similarity': {
286
+ if (!data.query.object) {
287
+ return errorResult('Object required for similarity query');
288
+ }
289
+ const objectEmb = stored.embeddings.get(data.query.object);
290
+ if (!objectEmb) {
291
+ return errorResult(`Object not found in taxonomy: ${data.query.object}`);
292
+ }
293
+ const distance = bridge.distance(subjectEmb, objectEmb);
294
+ const similarity = Math.exp(-distance);
295
+ result = similarity;
296
+ confidence = 1.0;
297
+ explanation = `Hyperbolic similarity: ${similarity.toFixed(4)} (distance: ${distance.toFixed(4)})`;
298
+ break;
299
+ }
300
+ }
301
+ const duration = performance.now() - startTime;
302
+ logger.info('Taxonomic reasoning completed', {
303
+ type: data.query.type,
304
+ confidence,
305
+ durationMs: duration.toFixed(2),
306
+ });
307
+ return successResult({
308
+ result,
309
+ confidence,
310
+ explanation,
311
+ steps,
312
+ queryType: data.query.type,
313
+ subject: data.query.subject,
314
+ object: data.query.object,
315
+ });
316
+ }
317
+ catch (error) {
318
+ logger.error('Taxonomic reasoning failed', { error: error instanceof Error ? error.message : String(error) });
319
+ return errorResult(error instanceof Error ? error : new Error(String(error)));
320
+ }
321
+ }
322
+ export const taxonomicReasonTool = {
323
+ name: 'hyperbolic_taxonomic_reason',
324
+ description: 'Taxonomic reasoning using hyperbolic entailment. Supports IS-A, subsumption, LCA, path, and similarity queries.',
325
+ category: 'hyperbolic',
326
+ version: '0.1.0',
327
+ tags: ['hyperbolic', 'taxonomy', 'reasoning', 'is-a', 'subsumption'],
328
+ cacheable: true,
329
+ cacheTTL: 60000,
330
+ inputSchema: {
331
+ type: 'object',
332
+ properties: {
333
+ query: {
334
+ type: 'object',
335
+ properties: {
336
+ type: { type: 'string', enum: ['is_a', 'subsumption', 'lowest_common_ancestor', 'path', 'similarity'] },
337
+ subject: { type: 'string' },
338
+ object: { type: 'string' },
339
+ },
340
+ },
341
+ taxonomy: { type: 'string', description: 'Taxonomy index ID from embed-hierarchy' },
342
+ inference: {
343
+ type: 'object',
344
+ properties: {
345
+ transitive: { type: 'boolean', default: true },
346
+ fuzzy: { type: 'boolean', default: false },
347
+ confidence: { type: 'number', default: 0.8 },
348
+ },
349
+ },
350
+ },
351
+ required: ['query', 'taxonomy'],
352
+ },
353
+ handler: taxonomicReasonHandler,
354
+ };
355
+ // ============================================================================
356
+ // Tool 3: hyperbolic/semantic-search
357
+ // ============================================================================
358
+ async function semanticSearchHandler(input, context) {
359
+ const logger = context?.logger ?? defaultLogger;
360
+ const startTime = performance.now();
361
+ try {
362
+ const validationResult = SemanticSearchInputSchema.safeParse(input);
363
+ if (!validationResult.success) {
364
+ return errorResult(`Invalid input: ${validationResult.error.message}`);
365
+ }
366
+ const data = validationResult.data;
367
+ logger.debug('Semantic search', { index: data.index, mode: data.searchMode, topK: data.topK });
368
+ // Get stored embedding
369
+ const stored = embeddingStore.get(data.index);
370
+ if (!stored) {
371
+ return errorResult(`Index not found: ${data.index}. Use hyperbolic_embed_hierarchy first.`);
372
+ }
373
+ const bridge = await getHyperbolicBridge();
374
+ // Create query embedding from text
375
+ // In production, use a proper text encoder
376
+ const queryEmb = new Float32Array(stored.dimension);
377
+ for (let i = 0; i < data.query.length; i++) {
378
+ const idx = data.query.charCodeAt(i) % stored.dimension;
379
+ queryEmb[idx] += 1;
380
+ }
381
+ const norm = Math.sqrt(queryEmb.reduce((s, v) => s + v * v, 0));
382
+ for (let i = 0; i < queryEmb.length; i++) {
383
+ queryEmb[i] = (queryEmb[i] / (norm + 1e-10)) * 0.5; // Scale to be inside ball
384
+ }
385
+ const queryPoint = {
386
+ coordinates: queryEmb,
387
+ curvature: stored.curvature,
388
+ dimension: stored.dimension,
389
+ };
390
+ const result = await bridge.search(queryPoint, data.index, data.topK, data.searchMode);
391
+ // Apply constraints
392
+ let filteredItems = result.items;
393
+ if (data.constraints?.maxDepth !== undefined) {
394
+ filteredItems = filteredItems.filter(item => {
395
+ const emb = stored.embeddings.get(item.id);
396
+ if (!emb)
397
+ return false;
398
+ return bridge.hierarchyDepth(emb) <= data.constraints.maxDepth;
399
+ });
400
+ }
401
+ if (data.constraints?.minDepth !== undefined) {
402
+ filteredItems = filteredItems.filter(item => {
403
+ const emb = stored.embeddings.get(item.id);
404
+ if (!emb)
405
+ return false;
406
+ return bridge.hierarchyDepth(emb) >= data.constraints.minDepth;
407
+ });
408
+ }
409
+ if (data.constraints?.subtreeRoot) {
410
+ const rootEmb = stored.embeddings.get(data.constraints.subtreeRoot);
411
+ if (rootEmb) {
412
+ filteredItems = filteredItems.filter(item => {
413
+ const emb = stored.embeddings.get(item.id);
414
+ if (!emb)
415
+ return false;
416
+ return bridge.isAncestor(rootEmb, emb);
417
+ });
418
+ }
419
+ }
420
+ const duration = performance.now() - startTime;
421
+ logger.info('Semantic search completed', {
422
+ results: filteredItems.length,
423
+ mode: data.searchMode,
424
+ durationMs: duration.toFixed(2),
425
+ });
426
+ return successResult({
427
+ items: filteredItems.slice(0, data.topK).map(item => ({
428
+ id: item.id,
429
+ distance: item.distance,
430
+ similarity: item.similarity,
431
+ depth: stored.embeddings.get(item.id)
432
+ ? bridge.hierarchyDepth(stored.embeddings.get(item.id))
433
+ : undefined,
434
+ })),
435
+ totalCandidates: result.totalCandidates,
436
+ searchTimeMs: result.searchTimeMs,
437
+ mode: data.searchMode,
438
+ });
439
+ }
440
+ catch (error) {
441
+ logger.error('Semantic search failed', { error: error instanceof Error ? error.message : String(error) });
442
+ return errorResult(error instanceof Error ? error : new Error(String(error)));
443
+ }
444
+ }
445
+ export const semanticSearchTool = {
446
+ name: 'hyperbolic_semantic_search',
447
+ description: 'Semantic search with hierarchical awareness. Supports nearest, subtree, ancestors, siblings, and cone search modes.',
448
+ category: 'hyperbolic',
449
+ version: '0.1.0',
450
+ tags: ['hyperbolic', 'search', 'semantic', 'hierarchy', 'nearest-neighbor'],
451
+ cacheable: true,
452
+ cacheTTL: 30000,
453
+ inputSchema: {
454
+ type: 'object',
455
+ properties: {
456
+ query: { type: 'string', description: 'Search query text' },
457
+ index: { type: 'string', description: 'Index ID from embed-hierarchy' },
458
+ searchMode: { type: 'string', enum: ['nearest', 'subtree', 'ancestors', 'siblings', 'cone'] },
459
+ constraints: {
460
+ type: 'object',
461
+ properties: {
462
+ maxDepth: { type: 'number' },
463
+ minDepth: { type: 'number' },
464
+ subtreeRoot: { type: 'string' },
465
+ },
466
+ },
467
+ topK: { type: 'number', default: 10 },
468
+ },
469
+ required: ['query', 'index'],
470
+ },
471
+ handler: semanticSearchHandler,
472
+ };
473
+ // ============================================================================
474
+ // Tool 4: hyperbolic/hierarchy-compare
475
+ // ============================================================================
476
+ async function hierarchyCompareHandler(input, context) {
477
+ const logger = context?.logger ?? defaultLogger;
478
+ const startTime = performance.now();
479
+ try {
480
+ const validationResult = HierarchyCompareInputSchema.safeParse(input);
481
+ if (!validationResult.success) {
482
+ return errorResult(`Invalid input: ${validationResult.error.message}`);
483
+ }
484
+ const data = validationResult.data;
485
+ logger.debug('Hierarchy compare', {
486
+ sourceNodes: data.source.nodes.length,
487
+ targetNodes: data.target.nodes.length,
488
+ method: data.alignment,
489
+ });
490
+ const bridge = await getHyperbolicBridge();
491
+ // Embed both hierarchies
492
+ const sourceEmb = await bridge.embedHierarchy(data.source);
493
+ const targetEmb = await bridge.embedHierarchy(data.target);
494
+ // Compute alignments
495
+ const alignments = [];
496
+ const matchedSource = new Set();
497
+ const matchedTarget = new Set();
498
+ // Greedy matching based on embedding similarity
499
+ const sourceIds = Array.from(sourceEmb.embeddings.keys());
500
+ const targetIds = Array.from(targetEmb.embeddings.keys());
501
+ for (const srcId of sourceIds) {
502
+ const srcPoint = sourceEmb.embeddings.get(srcId);
503
+ let bestTarget = '';
504
+ let bestSimilarity = -Infinity;
505
+ for (const tgtId of targetIds) {
506
+ if (matchedTarget.has(tgtId))
507
+ continue;
508
+ const tgtPoint = targetEmb.embeddings.get(tgtId);
509
+ // Compare embeddings (project to same curvature)
510
+ const dist = poincareDistance(srcPoint.coordinates, tgtPoint.coordinates, srcPoint.curvature);
511
+ const similarity = Math.exp(-dist);
512
+ if (similarity > bestSimilarity) {
513
+ bestSimilarity = similarity;
514
+ bestTarget = tgtId;
515
+ }
516
+ }
517
+ if (bestTarget && bestSimilarity > 0.5) {
518
+ alignments.push({
519
+ source: srcId,
520
+ target: bestTarget,
521
+ confidence: bestSimilarity,
522
+ });
523
+ matchedSource.add(srcId);
524
+ matchedTarget.add(bestTarget);
525
+ }
526
+ }
527
+ // Compute metrics
528
+ const metrics = {};
529
+ // Structural similarity: ratio of matched nodes
530
+ metrics['structural_similarity'] =
531
+ (alignments.length * 2) / (sourceIds.length + targetIds.length);
532
+ // Semantic similarity: average alignment confidence
533
+ metrics['semantic_similarity'] =
534
+ alignments.length > 0
535
+ ? alignments.reduce((s, a) => s + a.confidence, 0) / alignments.length
536
+ : 0;
537
+ // Coverage: ratio of source nodes matched
538
+ metrics['coverage'] = alignments.length / sourceIds.length;
539
+ // Precision: ratio of target nodes matched
540
+ metrics['precision'] = alignments.length / targetIds.length;
541
+ const unmatchedSource = sourceIds.filter(id => !matchedSource.has(id));
542
+ const unmatchedTarget = targetIds.filter(id => !matchedTarget.has(id));
543
+ const duration = performance.now() - startTime;
544
+ logger.info('Hierarchy comparison completed', {
545
+ alignments: alignments.length,
546
+ structuralSimilarity: metrics['structural_similarity'],
547
+ durationMs: duration.toFixed(2),
548
+ });
549
+ return successResult({
550
+ similarity: (metrics['structural_similarity'] + metrics['semantic_similarity']) / 2,
551
+ alignments,
552
+ metrics,
553
+ unmatchedSource,
554
+ unmatchedTarget,
555
+ method: data.alignment,
556
+ });
557
+ }
558
+ catch (error) {
559
+ logger.error('Hierarchy comparison failed', { error: error instanceof Error ? error.message : String(error) });
560
+ return errorResult(error instanceof Error ? error : new Error(String(error)));
561
+ }
562
+ }
563
+ export const hierarchyCompareTool = {
564
+ name: 'hyperbolic_hierarchy_compare',
565
+ description: 'Compare hierarchies using hyperbolic alignment. Computes structural and semantic similarity with node-level alignments.',
566
+ category: 'hyperbolic',
567
+ version: '0.1.0',
568
+ tags: ['hyperbolic', 'comparison', 'alignment', 'tree-edit', 'similarity'],
569
+ cacheable: true,
570
+ cacheTTL: 120000,
571
+ inputSchema: {
572
+ type: 'object',
573
+ properties: {
574
+ source: { type: 'object', description: 'First hierarchy' },
575
+ target: { type: 'object', description: 'Second hierarchy' },
576
+ alignment: {
577
+ type: 'string',
578
+ enum: ['wasserstein', 'gromov_wasserstein', 'tree_edit', 'subtree_isomorphism'],
579
+ },
580
+ metrics: {
581
+ type: 'array',
582
+ items: { type: 'string', enum: ['structural_similarity', 'semantic_similarity', 'coverage', 'precision'] },
583
+ },
584
+ },
585
+ required: ['source', 'target'],
586
+ },
587
+ handler: hierarchyCompareHandler,
588
+ };
589
+ // ============================================================================
590
+ // Tool 5: hyperbolic/entailment-graph
591
+ // ============================================================================
592
+ async function entailmentGraphHandler(input, context) {
593
+ const logger = context?.logger ?? defaultLogger;
594
+ const startTime = performance.now();
595
+ try {
596
+ const validationResult = EntailmentGraphInputSchema.safeParse(input);
597
+ if (!validationResult.success) {
598
+ return errorResult(`Invalid input: ${validationResult.error.message}`);
599
+ }
600
+ const data = validationResult.data;
601
+ logger.debug('Entailment graph', { action: data.action, concepts: data.concepts?.length });
602
+ const gnn = await getGnnBridge();
603
+ switch (data.action) {
604
+ case 'build': {
605
+ if (!data.concepts || data.concepts.length === 0) {
606
+ return errorResult('Concepts required for build action');
607
+ }
608
+ const graph = await gnn.buildEntailmentGraph(data.concepts, data.entailmentThreshold);
609
+ // Apply transitive closure if requested
610
+ const finalGraph = data.transitiveClosure
611
+ ? gnn.computeTransitiveClosure(graph)
612
+ : graph;
613
+ const duration = performance.now() - startTime;
614
+ logger.info('Entailment graph built', {
615
+ nodes: finalGraph.stats.nodeCount,
616
+ edges: finalGraph.stats.edgeCount,
617
+ durationMs: duration.toFixed(2),
618
+ });
619
+ return successResult({
620
+ graphId: `entailment_${Date.now()}`,
621
+ stats: finalGraph.stats,
622
+ relations: finalGraph.relations.slice(0, 50), // Sample
623
+ transitiveClosure: finalGraph.transitiveClosure,
624
+ });
625
+ }
626
+ case 'query': {
627
+ if (!data.query?.premise) {
628
+ return errorResult('Premise required for query action');
629
+ }
630
+ // Would query stored graph - simplified for demo
631
+ return successResult({
632
+ query: data.query,
633
+ results: [],
634
+ message: 'Query requires pre-built graph. Use build action first.',
635
+ });
636
+ }
637
+ case 'prune': {
638
+ if (!data.graphId) {
639
+ return errorResult('GraphId required for prune action');
640
+ }
641
+ // Simplified - would prune stored graph
642
+ return successResult({
643
+ graphId: data.graphId,
644
+ pruneStrategy: data.pruneStrategy,
645
+ message: 'Prune action completed',
646
+ });
647
+ }
648
+ case 'expand': {
649
+ if (!data.concepts) {
650
+ return errorResult('Concepts required for expand action');
651
+ }
652
+ return successResult({
653
+ added: data.concepts.length,
654
+ message: 'Expand action completed',
655
+ });
656
+ }
657
+ default:
658
+ return errorResult(`Unknown action: ${data.action}`);
659
+ }
660
+ }
661
+ catch (error) {
662
+ logger.error('Entailment graph operation failed', { error: error instanceof Error ? error.message : String(error) });
663
+ return errorResult(error instanceof Error ? error : new Error(String(error)));
664
+ }
665
+ }
666
+ export const entailmentGraphTool = {
667
+ name: 'hyperbolic_entailment_graph',
668
+ description: 'Build and query entailment graphs using hyperbolic embeddings. Supports transitive closure and pruning strategies.',
669
+ category: 'hyperbolic',
670
+ version: '0.1.0',
671
+ tags: ['hyperbolic', 'entailment', 'graph', 'nli', 'reasoning'],
672
+ cacheable: false,
673
+ inputSchema: {
674
+ type: 'object',
675
+ properties: {
676
+ action: { type: 'string', enum: ['build', 'query', 'expand', 'prune'] },
677
+ concepts: {
678
+ type: 'array',
679
+ items: {
680
+ type: 'object',
681
+ properties: {
682
+ id: { type: 'string' },
683
+ text: { type: 'string' },
684
+ type: { type: 'string' },
685
+ },
686
+ },
687
+ },
688
+ graphId: { type: 'string' },
689
+ query: {
690
+ type: 'object',
691
+ properties: {
692
+ premise: { type: 'string' },
693
+ hypothesis: { type: 'string' },
694
+ },
695
+ },
696
+ entailmentThreshold: { type: 'number', default: 0.7 },
697
+ transitiveClosure: { type: 'boolean', default: true },
698
+ pruneStrategy: { type: 'string', enum: ['none', 'transitive_reduction', 'confidence_threshold'] },
699
+ },
700
+ required: ['action'],
701
+ },
702
+ handler: entailmentGraphHandler,
703
+ };
704
+ // ============================================================================
705
+ // Tool Exports
706
+ // ============================================================================
707
+ /**
708
+ * All Hyperbolic Reasoning MCP Tools
709
+ */
710
+ export const hyperbolicReasoningTools = [
711
+ embedHierarchyTool,
712
+ taxonomicReasonTool,
713
+ semanticSearchTool,
714
+ hierarchyCompareTool,
715
+ entailmentGraphTool,
716
+ ];
717
+ /**
718
+ * Tool name to handler map
719
+ */
720
+ export const toolHandlers = new Map([
721
+ ['hyperbolic_embed_hierarchy', embedHierarchyHandler],
722
+ ['hyperbolic_taxonomic_reason', taxonomicReasonHandler],
723
+ ['hyperbolic_semantic_search', semanticSearchHandler],
724
+ ['hyperbolic_hierarchy_compare', hierarchyCompareHandler],
725
+ ['hyperbolic_entailment_graph', entailmentGraphHandler],
726
+ ]);
727
+ /**
728
+ * Get a tool by name
729
+ */
730
+ export function getTool(name) {
731
+ return hyperbolicReasoningTools.find(t => t.name === name);
732
+ }
733
+ /**
734
+ * Get all tool names
735
+ */
736
+ export function getToolNames() {
737
+ return hyperbolicReasoningTools.map(t => t.name);
738
+ }
739
+ export default hyperbolicReasoningTools;
740
+ //# sourceMappingURL=mcp-tools.js.map