ruvector 0.2.18 → 0.2.20

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.
package/bin/mcp-server.js CHANGED
@@ -95,71 +95,6 @@ function sanitizeNumericArg(arg, defaultVal) {
95
95
  return Number.isFinite(n) && n > 0 ? n : (defaultVal || 0);
96
96
  }
97
97
 
98
- // ── Proxy-aware fetch wrapper ───────────────────────────────────────────────
99
- let _proxyDispatcherSet = false;
100
-
101
- function getProxyUrl(targetUrl) {
102
- const NO_PROXY = process.env.NO_PROXY || process.env.no_proxy || '';
103
- if (NO_PROXY === '*') return null;
104
- if (NO_PROXY) {
105
- try {
106
- const host = new URL(targetUrl).hostname.toLowerCase();
107
- const patterns = NO_PROXY.split(',').map(p => p.trim().toLowerCase());
108
- for (const p of patterns) {
109
- if (!p) continue;
110
- if (host === p || host.endsWith(p.startsWith('.') ? p : '.' + p)) return null;
111
- }
112
- } catch {}
113
- }
114
- return process.env.HTTPS_PROXY || process.env.https_proxy
115
- || process.env.HTTP_PROXY || process.env.http_proxy
116
- || process.env.ALL_PROXY || process.env.all_proxy
117
- || null;
118
- }
119
-
120
- async function proxyFetch(url, opts) {
121
- const target = typeof url === 'string' ? url : url.toString();
122
- const proxy = getProxyUrl(target);
123
- if (!proxy) return fetch(url, opts);
124
-
125
- if (!_proxyDispatcherSet) {
126
- try {
127
- const undici = require('undici');
128
- if (undici.ProxyAgent && undici.setGlobalDispatcher) {
129
- undici.setGlobalDispatcher(new undici.ProxyAgent(proxy));
130
- _proxyDispatcherSet = true;
131
- }
132
- } catch {}
133
- }
134
- if (_proxyDispatcherSet) return fetch(url, opts);
135
-
136
- const { execFileSync } = require('child_process');
137
- const args = ['-sS', '-L', '--max-time', '30'];
138
- if (opts && opts.method) { args.push('-X', opts.method); }
139
- if (opts && opts.headers) {
140
- for (const [k, v] of Object.entries(opts.headers)) {
141
- args.push('-H', `${k}: ${v}`);
142
- }
143
- }
144
- if (opts && opts.body) { args.push('-d', typeof opts.body === 'string' ? opts.body : JSON.stringify(opts.body)); }
145
- args.push(target);
146
- try {
147
- const stdout = execFileSync('curl', args, { encoding: 'utf8', timeout: 35000 });
148
- const body = stdout.trim();
149
- return {
150
- ok: true,
151
- status: 200,
152
- statusText: 'OK',
153
- text: async () => body,
154
- json: async () => JSON.parse(body),
155
- headers: new Map(),
156
- };
157
- } catch (e) {
158
- const msg = e.stderr ? e.stderr.toString().trim() : e.message;
159
- throw new Error(`Proxy curl failed: ${msg}`);
160
- }
161
- }
162
-
163
98
  // Try to load the full IntelligenceEngine
164
99
  let IntelligenceEngine = null;
165
100
  let engineAvailable = false;
@@ -428,7 +363,7 @@ class Intelligence {
428
363
  const server = new Server(
429
364
  {
430
365
  name: 'ruvector',
431
- version: '0.2.16',
366
+ version: '0.2.0',
432
367
  },
433
368
  {
434
369
  capabilities: {
@@ -1289,12 +1224,11 @@ const TOOLS = [
1289
1224
  },
1290
1225
  {
1291
1226
  name: 'rvf_examples',
1292
- description: 'List available example .rvf files with download URLs. Supports filtering by name, description, or category.',
1227
+ description: 'List available example .rvf files with download URLs from the ruvector repository',
1293
1228
  inputSchema: {
1294
1229
  type: 'object',
1295
1230
  properties: {
1296
- filter: { type: 'string', description: 'Filter examples by name or description substring' },
1297
- category: { type: 'string', description: 'Filter by category (core, ai, security, compute, lineage, industry, network, integration)' }
1231
+ filter: { type: 'string', description: 'Filter examples by name or description substring' }
1298
1232
  },
1299
1233
  required: []
1300
1234
  }
@@ -1336,7 +1270,8 @@ const TOOLS = [
1336
1270
  required: ['query']
1337
1271
  }
1338
1272
  },
1339
- // ── Brain Tools (11) ── Shared intelligence via @ruvector/pi-brain ──
1273
+
1274
+ // ── Brain Tools (Shared Intelligence) ─────────────────────────────────
1340
1275
  {
1341
1276
  name: 'brain_search',
1342
1277
  description: 'Semantic search across shared brain knowledge',
@@ -1344,67 +1279,64 @@ const TOOLS = [
1344
1279
  type: 'object',
1345
1280
  properties: {
1346
1281
  query: { type: 'string', description: 'Search query' },
1347
- category: { type: 'string', description: 'Filter by category (pattern, solution, architecture, convention, security, performance, tooling)' },
1348
- limit: { type: 'number', description: 'Max results (default 10)' }
1282
+ limit: { type: 'number', description: 'Max results to return', default: 10 },
1283
+ category: { type: 'string', description: 'Filter by category (optional)' }
1349
1284
  },
1350
1285
  required: ['query']
1351
1286
  }
1352
1287
  },
1353
1288
  {
1354
1289
  name: 'brain_share',
1355
- description: 'Share a learning or pattern with the collective brain',
1290
+ description: 'Share knowledge with the collective brain',
1356
1291
  inputSchema: {
1357
1292
  type: 'object',
1358
1293
  properties: {
1359
1294
  title: { type: 'string', description: 'Title of the knowledge entry' },
1360
- content: { type: 'string', description: 'Content/description of the knowledge' },
1361
- category: { type: 'string', description: 'Category (pattern, solution, architecture, convention, security, performance, tooling)' },
1362
- tags: { type: 'string', description: 'Comma-separated tags' },
1363
- code_snippet: { type: 'string', description: 'Optional code snippet' }
1295
+ content: { type: 'string', description: 'Knowledge content to share' },
1296
+ category: { type: 'string', description: 'Category (pattern, architecture, security, etc.)', default: 'pattern' },
1297
+ tags: { type: 'array', items: { type: 'string' }, description: 'Tags for the entry' }
1364
1298
  },
1365
- required: ['title', 'content', 'category']
1299
+ required: ['title', 'content']
1366
1300
  }
1367
1301
  },
1368
1302
  {
1369
1303
  name: 'brain_get',
1370
- description: 'Retrieve a specific memory by ID with full provenance',
1304
+ description: 'Retrieve a specific memory by ID with provenance',
1371
1305
  inputSchema: {
1372
1306
  type: 'object',
1373
1307
  properties: {
1374
- id: { type: 'string', description: 'Memory ID' }
1308
+ id: { type: 'string', description: 'Memory ID to retrieve' }
1375
1309
  },
1376
1310
  required: ['id']
1377
1311
  }
1378
1312
  },
1379
1313
  {
1380
1314
  name: 'brain_vote',
1381
- description: 'Quality-gate a memory with an up or down vote',
1315
+ description: 'Vote on knowledge quality',
1382
1316
  inputSchema: {
1383
1317
  type: 'object',
1384
1318
  properties: {
1385
- id: { type: 'string', description: 'Memory ID' },
1386
- direction: { type: 'string', description: 'Vote direction: up or down' }
1319
+ id: { type: 'string', description: 'Memory ID to vote on' },
1320
+ direction: { type: 'string', enum: ['up', 'down'], description: 'Vote direction' }
1387
1321
  },
1388
1322
  required: ['id', 'direction']
1389
1323
  }
1390
1324
  },
1391
1325
  {
1392
1326
  name: 'brain_list',
1393
- description: 'List recent shared memories filtered by category or quality',
1327
+ description: 'List recent shared memories',
1394
1328
  inputSchema: {
1395
1329
  type: 'object',
1396
1330
  properties: {
1397
- category: { type: 'string', description: 'Filter by category' },
1398
- limit: { type: 'number', description: 'Max results (default 20)' },
1399
- offset: { type: 'number', description: 'Skip first N results for pagination (default 0)' },
1400
- sort: { type: 'string', description: 'Sort by: updated_at, quality, votes (default updated_at)' },
1401
- tags: { type: 'string', description: 'Filter by tags (comma-separated)' }
1402
- }
1331
+ category: { type: 'string', description: 'Filter by category (optional)' },
1332
+ limit: { type: 'number', description: 'Max results to return', default: 20 }
1333
+ },
1334
+ required: []
1403
1335
  }
1404
1336
  },
1405
1337
  {
1406
1338
  name: 'brain_delete',
1407
- description: 'Delete your own contribution from the shared brain',
1339
+ description: 'Delete your own contribution',
1408
1340
  inputSchema: {
1409
1341
  type: 'object',
1410
1342
  properties: {
@@ -1415,302 +1347,200 @@ const TOOLS = [
1415
1347
  },
1416
1348
  {
1417
1349
  name: 'brain_status',
1418
- description: 'Get shared brain system health: counts, drift, quality, graph topology',
1350
+ description: 'Get brain system health and statistics',
1419
1351
  inputSchema: {
1420
1352
  type: 'object',
1421
- properties: {}
1353
+ properties: {},
1354
+ required: []
1422
1355
  }
1423
1356
  },
1424
1357
  {
1425
1358
  name: 'brain_drift',
1426
- description: 'Check if shared knowledge has drifted from local state',
1359
+ description: 'Check knowledge drift',
1427
1360
  inputSchema: {
1428
1361
  type: 'object',
1429
1362
  properties: {
1430
- domain: { type: 'string', description: 'Domain to check drift for' }
1431
- }
1363
+ domain: { type: 'string', description: 'Domain to check drift for (optional)' }
1364
+ },
1365
+ required: []
1432
1366
  }
1433
1367
  },
1434
1368
  {
1435
1369
  name: 'brain_partition',
1436
- description: 'Get knowledge partitioned by mincut topology into clusters',
1370
+ description: 'Get knowledge topology via mincut',
1437
1371
  inputSchema: {
1438
1372
  type: 'object',
1439
1373
  properties: {
1440
- domain: { type: 'string', description: 'Domain to partition' },
1441
- min_cluster_size: { type: 'number', description: 'Minimum cluster size (default 3)' }
1442
- }
1374
+ domain: { type: 'string', description: 'Domain to partition (optional)' },
1375
+ min_cluster_size: { type: 'number', description: 'Minimum cluster size', default: 3 }
1376
+ },
1377
+ required: []
1443
1378
  }
1444
1379
  },
1445
1380
  {
1446
1381
  name: 'brain_transfer',
1447
- description: 'Apply learned priors from one knowledge domain to another',
1382
+ description: 'Transfer learned priors between domains',
1448
1383
  inputSchema: {
1449
1384
  type: 'object',
1450
1385
  properties: {
1451
- source_domain: { type: 'string', description: 'Source domain to transfer from' },
1452
- target_domain: { type: 'string', description: 'Target domain to transfer to' }
1386
+ source: { type: 'string', description: 'Source domain' },
1387
+ target: { type: 'string', description: 'Target domain' }
1453
1388
  },
1454
- required: ['source_domain', 'target_domain']
1389
+ required: ['source', 'target']
1455
1390
  }
1456
1391
  },
1457
1392
  {
1458
1393
  name: 'brain_sync',
1459
- description: 'Synchronize LoRA weights between local and shared brain',
1394
+ description: 'Sync LoRA weights',
1460
1395
  inputSchema: {
1461
1396
  type: 'object',
1462
1397
  properties: {
1463
- direction: { type: 'string', description: 'Sync direction: pull, push, or both (default both)' }
1464
- }
1465
- }
1466
- },
1467
- {
1468
- name: 'brain_train',
1469
- description: 'Trigger a training cycle — runs SONA pattern learning and domain evolution on accumulated data',
1470
- inputSchema: { type: 'object', properties: {} }
1471
- },
1472
- // ── Brain AGI Tools (6) ── AGI subsystem diagnostics via direct fetch ──
1473
- {
1474
- name: 'brain_agi_status',
1475
- description: 'Combined AGI subsystem diagnostics — SONA, GWT, temporal, meta-learning, midstream',
1476
- inputSchema: { type: 'object', properties: {} }
1477
- },
1478
- {
1479
- name: 'brain_sona_stats',
1480
- description: 'SONA learning engine stats — patterns, trajectories, background ticks',
1481
- inputSchema: { type: 'object', properties: {} }
1482
- },
1483
- {
1484
- name: 'brain_temporal',
1485
- description: 'Temporal delta tracking — velocity, trend, total deltas',
1486
- inputSchema: { type: 'object', properties: {} }
1487
- },
1488
- {
1489
- name: 'brain_explore',
1490
- description: 'Meta-learning exploration — curiosity, regret, plateau status, Pareto frontier',
1491
- inputSchema: { type: 'object', properties: {} }
1492
- },
1493
- {
1494
- name: 'brain_midstream',
1495
- description: 'Midstream platform diagnostics — scheduler, attractor, solver, strange-loop',
1496
- inputSchema: { type: 'object', properties: {} }
1497
- },
1498
- {
1499
- name: 'brain_flags',
1500
- description: 'Show backend feature flag state (RVF, AGI, midstream flags)',
1501
- inputSchema: { type: 'object', properties: {} }
1502
- },
1503
- // ── Brainpedia Page Tools (5) ── Knowledge page management ──
1504
- {
1505
- name: 'brain_page_list',
1506
- description: 'List Brainpedia knowledge pages with pagination and status filter',
1507
- inputSchema: {
1508
- type: 'object',
1509
- properties: {
1510
- limit: { type: 'number', description: 'Max pages to return (default 20)' },
1511
- offset: { type: 'number', description: 'Skip first N pages for pagination (default 0)' },
1512
- status: { type: 'string', description: 'Filter by status: draft, canonical, contested, archived' }
1513
- }
1514
- }
1515
- },
1516
- {
1517
- name: 'brain_page_get',
1518
- description: 'Get a Brainpedia page by ID with delta log and evidence links',
1519
- inputSchema: {
1520
- type: 'object',
1521
- properties: {
1522
- id: { type: 'string', description: 'Page ID (UUID or slug)' }
1398
+ direction: { type: 'string', enum: ['pull', 'push', 'both'], description: 'Sync direction', default: 'both' }
1523
1399
  },
1524
- required: ['id']
1400
+ required: []
1525
1401
  }
1526
1402
  },
1403
+
1404
+ // ── Edge Tools (Distributed Compute) ──────────────────────────────────
1527
1405
  {
1528
- name: 'brain_page_create',
1529
- description: 'Create a new Brainpedia knowledge page (requires reputation >= 0.5)',
1406
+ name: 'edge_status',
1407
+ description: 'Query edge network status',
1530
1408
  inputSchema: {
1531
1409
  type: 'object',
1532
- properties: {
1533
- title: { type: 'string', description: 'Page title' },
1534
- content: { type: 'string', description: 'Page content body' },
1535
- category: { type: 'string', description: 'Category (pattern, solution, architecture, convention, security, performance, tooling)' },
1536
- tags: { type: 'string', description: 'Comma-separated tags' },
1537
- code_snippet: { type: 'string', description: 'Optional code snippet' }
1538
- },
1539
- required: ['title', 'content', 'category']
1410
+ properties: {},
1411
+ required: []
1540
1412
  }
1541
1413
  },
1542
1414
  {
1543
- name: 'brain_page_update',
1544
- description: 'Submit a delta (correction, extension, evidence) to a Brainpedia page',
1415
+ name: 'edge_join',
1416
+ description: 'Join as compute node',
1545
1417
  inputSchema: {
1546
1418
  type: 'object',
1547
1419
  properties: {
1548
- page_id: { type: 'string', description: 'Page ID to update' },
1549
- delta_type: { type: 'string', description: 'Delta type: correction, extension, evidence, deprecation' },
1550
- content_diff: { type: 'string', description: 'Content diff or new content' },
1551
- evidence_links: { type: 'string', description: 'JSON array of evidence links' }
1420
+ contribution: { type: 'number', description: 'Contribution factor (0-1)', default: 0.3 },
1421
+ key: { type: 'string', description: 'PI key (optional, defaults to PI env var)' }
1552
1422
  },
1553
- required: ['page_id', 'content_diff']
1423
+ required: []
1554
1424
  }
1555
1425
  },
1556
1426
  {
1557
- name: 'brain_page_delete',
1558
- description: 'Delete a Brainpedia page by ID',
1427
+ name: 'edge_balance',
1428
+ description: 'Check rUv balance',
1559
1429
  inputSchema: {
1560
1430
  type: 'object',
1561
1431
  properties: {
1562
- id: { type: 'string', description: 'Page ID to delete' }
1432
+ key: { type: 'string', description: 'PI key (optional, defaults to PI env var)' }
1563
1433
  },
1564
- required: ['id']
1434
+ required: []
1565
1435
  }
1566
1436
  },
1567
- // ── WASM Node Tools (4) ── Executable compute node management ──
1568
1437
  {
1569
- name: 'brain_node_list',
1570
- description: 'List published WASM compute nodes',
1438
+ name: 'edge_tasks',
1439
+ description: 'List available distributed compute tasks',
1571
1440
  inputSchema: {
1572
1441
  type: 'object',
1573
- properties: {
1574
- limit: { type: 'number', description: 'Max nodes to return (default 20)' }
1575
- }
1442
+ properties: {},
1443
+ required: []
1576
1444
  }
1577
1445
  },
1446
+
1447
+ // ── Identity Tools (PI Key Management) ────────────────────────────────
1578
1448
  {
1579
- name: 'brain_node_get',
1580
- description: 'Get WASM compute node metadata and conformance vectors',
1449
+ name: 'identity_generate',
1450
+ description: 'Generate a new PI key with SHAKE-256 pseudonym',
1581
1451
  inputSchema: {
1582
1452
  type: 'object',
1583
- properties: {
1584
- id: { type: 'string', description: 'Node ID' }
1585
- },
1586
- required: ['id']
1453
+ properties: {},
1454
+ required: []
1587
1455
  }
1588
1456
  },
1589
1457
  {
1590
- name: 'brain_node_publish',
1591
- description: 'Publish a WASM compute node to the shared brain network',
1458
+ name: 'identity_show',
1459
+ description: 'Show current identity derived from PI key',
1592
1460
  inputSchema: {
1593
1461
  type: 'object',
1594
1462
  properties: {
1595
- name: { type: 'string', description: 'Node name' },
1596
- wasm_base64: { type: 'string', description: 'Base64-encoded WASM binary' },
1597
- description: { type: 'string', description: 'Node description' },
1598
- conformance_vectors: { type: 'string', description: 'JSON array of conformance test vectors' }
1463
+ key: { type: 'string', description: 'PI key (optional, defaults to PI env var)' }
1599
1464
  },
1600
- required: ['name', 'wasm_base64']
1465
+ required: []
1601
1466
  }
1602
1467
  },
1468
+
1469
+ // ── Decompiler Tools ───────────────────────────────────────────────────
1603
1470
  {
1604
- name: 'brain_node_revoke',
1605
- description: 'Revoke a published WASM compute node',
1471
+ name: 'decompile_package',
1472
+ description: 'Decompile an npm package. Fetches from registry, extracts bundle, splits into modules, computes metrics and witness chain.',
1606
1473
  inputSchema: {
1607
1474
  type: 'object',
1608
1475
  properties: {
1609
- id: { type: 'string', description: 'Node ID to revoke' }
1476
+ package: { type: 'string', description: 'npm package name (e.g. "express", "@anthropic-ai/claude-code")' },
1477
+ version: { type: 'string', description: 'Version (default: latest)' },
1478
+ min_confidence: { type: 'number', description: 'Minimum confidence threshold (0-1, default: 0.3)' }
1610
1479
  },
1611
- required: ['id']
1480
+ required: ['package']
1612
1481
  }
1613
1482
  },
1614
- // ── Midstream Tools (6) ── Real-time streaming analysis platform ──
1615
- {
1616
- name: 'midstream_status',
1617
- description: 'Full midstream platform diagnostics — scheduler, attractor, solver, strange-loop',
1618
- inputSchema: { type: 'object', properties: {} }
1619
- },
1620
1483
  {
1621
- name: 'midstream_attractor',
1622
- description: 'Attractor categories with Lyapunov exponent analysis',
1484
+ name: 'decompile_file',
1485
+ description: 'Decompile a local JavaScript file. Beautifies, splits into modules, computes metrics.',
1623
1486
  inputSchema: {
1624
1487
  type: 'object',
1625
1488
  properties: {
1626
- category: { type: 'string', description: 'Optional category to filter (e.g., pattern, solution)' }
1627
- }
1489
+ path: { type: 'string', description: 'Path to .js file' },
1490
+ min_confidence: { type: 'number', description: 'Minimum confidence threshold (0-1, default: 0.3)' }
1491
+ },
1492
+ required: ['path']
1628
1493
  }
1629
1494
  },
1630
1495
  {
1631
- name: 'midstream_scheduler',
1632
- description: 'Nanosecond scheduler performance metrics ticks, tasks/sec',
1633
- inputSchema: { type: 'object', properties: {} }
1634
- },
1635
- {
1636
- name: 'midstream_benchmark',
1637
- description: 'Run sequential + concurrent latency benchmark against brain backend',
1496
+ name: 'decompile_url',
1497
+ description: 'Decompile JavaScript from a URL (unpkg, CDN, raw GitHub, etc).',
1638
1498
  inputSchema: {
1639
1499
  type: 'object',
1640
1500
  properties: {
1641
- concurrent_count: { type: 'number', description: 'Number of concurrent search requests (default 20, max 100)' }
1642
- }
1501
+ url: { type: 'string', description: 'URL to fetch JavaScript from' },
1502
+ min_confidence: { type: 'number', description: 'Minimum confidence threshold (0-1, default: 0.3)' }
1503
+ },
1504
+ required: ['url']
1643
1505
  }
1644
1506
  },
1645
1507
  {
1646
- name: 'midstream_search',
1647
- description: 'Semantic search with midstream scoring metadata in response',
1508
+ name: 'decompile_search',
1509
+ description: 'Search decompiled code for patterns, function names, or string literals.',
1648
1510
  inputSchema: {
1649
1511
  type: 'object',
1650
1512
  properties: {
1651
- query: { type: 'string', description: 'Search query' },
1652
- limit: { type: 'number', description: 'Max results (default 10)' }
1513
+ query: { type: 'string', description: 'Search query (regex supported)' },
1514
+ package: { type: 'string', description: 'npm package to decompile and search' },
1515
+ version: { type: 'string', description: 'Package version (default: latest)' },
1516
+ path: { type: 'string', description: 'Local file path to decompile and search (alternative to package)' }
1653
1517
  },
1654
1518
  required: ['query']
1655
1519
  }
1656
1520
  },
1657
1521
  {
1658
- name: 'midstream_health',
1659
- description: 'Combined health + midstream subsystem check',
1660
- inputSchema: { type: 'object', properties: {} }
1661
- },
1662
- // ── Edge Tools (4) ── Distributed compute via @ruvector/edge-net ──
1663
- {
1664
- name: 'edge_status',
1665
- description: 'Get edge compute network status (genesis, relay, nodes, rUv supply)',
1666
- inputSchema: {
1667
- type: 'object',
1668
- properties: {}
1669
- }
1670
- },
1671
- {
1672
- name: 'edge_join',
1673
- description: 'Join the edge compute network as a compute node',
1522
+ name: 'decompile_diff',
1523
+ description: 'Compare decompiled output between two versions of an npm package. Shows added/removed/changed modules.',
1674
1524
  inputSchema: {
1675
1525
  type: 'object',
1676
1526
  properties: {
1677
- contribution: { type: 'number', description: 'Contribution level 0.0-1.0 (default 0.3)' }
1678
- }
1679
- }
1680
- },
1681
- {
1682
- name: 'edge_balance',
1683
- description: 'Check rUv credit balance for current identity',
1684
- inputSchema: {
1685
- type: 'object',
1686
- properties: {}
1527
+ package: { type: 'string', description: 'npm package name' },
1528
+ version_a: { type: 'string', description: 'First version' },
1529
+ version_b: { type: 'string', description: 'Second version' }
1530
+ },
1531
+ required: ['package', 'version_a', 'version_b']
1687
1532
  }
1688
1533
  },
1689
1534
  {
1690
- name: 'edge_tasks',
1691
- description: 'List available distributed compute tasks on the edge network',
1535
+ name: 'decompile_witness',
1536
+ description: 'Verify the cryptographic witness chain of a decompilation. Proves output derives faithfully from input.',
1692
1537
  inputSchema: {
1693
1538
  type: 'object',
1694
1539
  properties: {
1695
- limit: { type: 'number', description: 'Max tasks to return (default 20)' }
1696
- }
1697
- }
1698
- },
1699
- // ── Identity Tools (2) ── Pi key management ──
1700
- {
1701
- name: 'identity_generate',
1702
- description: 'Generate a new pi key and derive pseudonym',
1703
- inputSchema: {
1704
- type: 'object',
1705
- properties: {}
1706
- }
1707
- },
1708
- {
1709
- name: 'identity_show',
1710
- description: 'Show current pi key pseudonym and derived identities',
1711
- inputSchema: {
1712
- type: 'object',
1713
- properties: {}
1540
+ witness_path: { type: 'string', description: 'Path to witness.json file' },
1541
+ source_path: { type: 'string', description: 'Path to original bundle (optional, for source hash verification)' }
1542
+ },
1543
+ required: ['witness_path']
1714
1544
  }
1715
1545
  }
1716
1546
  ];
@@ -3054,15 +2884,9 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
3054
2884
  }
3055
2885
 
3056
2886
  case 'workers_create': {
3057
- const name = sanitizeShellArg(args.name);
3058
- const preset = sanitizeShellArg(args.preset || 'quick-scan');
3059
- const triggers = args.triggers ? sanitizeShellArg(args.triggers) : null;
3060
- if (!name) {
3061
- return { content: [{ type: 'text', text: JSON.stringify({
3062
- success: false,
3063
- error: 'Invalid worker name'
3064
- }, null, 2) }] };
3065
- }
2887
+ const name = args.name;
2888
+ const preset = args.preset || 'quick-scan';
2889
+ const triggers = args.triggers;
3066
2890
  try {
3067
2891
  let cmd = `npx agentic-flow@alpha workers create "${name}" --preset ${preset}`;
3068
2892
  if (triggers) cmd += ` --triggers "${triggers}"`;
@@ -3292,85 +3116,31 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
3292
3116
  }
3293
3117
 
3294
3118
  case 'rvf_examples': {
3295
- const os = require('os');
3296
- const GCS_MANIFEST = 'https://storage.googleapis.com/ruvector-examples/manifest.json';
3297
- const GITHUB_RAW = 'https://raw.githubusercontent.com/ruvnet/ruvector/main/examples/rvf/output';
3298
- const cacheDir = path.join(os.homedir(), '.ruvector', 'examples');
3299
- const manifestPath = path.join(cacheDir, 'manifest.json');
3300
-
3301
- let manifest;
3302
- // Try cache first
3303
- if (fs.existsSync(manifestPath)) {
3304
- try {
3305
- const stat = fs.statSync(manifestPath);
3306
- if (Date.now() - stat.mtimeMs < 3600000) {
3307
- manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
3308
- }
3309
- } catch {}
3310
- }
3311
-
3312
- // Fetch from GCS if no fresh cache
3313
- if (!manifest) {
3314
- try {
3315
- const resp = await proxyFetch(GCS_MANIFEST, { signal: AbortSignal.timeout(15000) });
3316
- if (resp.ok) {
3317
- manifest = await resp.json();
3318
- try {
3319
- fs.mkdirSync(cacheDir, { recursive: true });
3320
- fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));
3321
- } catch {}
3322
- }
3323
- } catch {}
3324
- }
3325
-
3326
- // Fallback to hardcoded
3327
- if (!manifest) {
3328
- manifest = {
3329
- version: 'builtin',
3330
- base_url: GITHUB_RAW,
3331
- examples: [
3332
- { name: 'basic_store', size_human: '152 KB', description: '1,000 vectors, dim 128', category: 'core' },
3333
- { name: 'semantic_search', size_human: '755 KB', description: 'Semantic search with HNSW', category: 'core' },
3334
- { name: 'rag_pipeline', size_human: '303 KB', description: 'RAG pipeline embeddings', category: 'core' },
3335
- { name: 'agent_memory', size_human: '32 KB', description: 'AI agent episodic memory', category: 'ai' },
3336
- { name: 'swarm_knowledge', size_human: '86 KB', description: 'Multi-agent knowledge base', category: 'ai' },
3337
- { name: 'self_booting', size_human: '31 KB', description: 'Self-booting with kernel', category: 'compute' },
3338
- { name: 'ebpf_accelerator', size_human: '153 KB', description: 'eBPF distance accelerator', category: 'compute' },
3339
- { name: 'tee_attestation', size_human: '102 KB', description: 'TEE attestation + witnesses', category: 'security' },
3340
- { name: 'claude_code_appliance', size_human: '17 KB', description: 'Claude Code appliance', category: 'integration' },
3341
- { name: 'lineage_parent', size_human: '52 KB', description: 'COW parent file', category: 'lineage' },
3342
- { name: 'financial_signals', size_human: '202 KB', description: 'Financial signals', category: 'industry' },
3343
- { name: 'progressive_index', size_human: '2.5 MB', description: 'Large-scale HNSW index', category: 'core' },
3344
- ]
3345
- };
3346
- }
3347
-
3348
- let examples = manifest.examples || [];
3349
- const baseUrl = manifest.base_url || GITHUB_RAW;
3350
-
3119
+ const BASE_URL = 'https://raw.githubusercontent.com/ruvnet/ruvector/main/examples/rvf/output';
3120
+ const examples = [
3121
+ { name: 'basic_store', size: '152 KB', desc: '1,000 vectors, dim 128' },
3122
+ { name: 'semantic_search', size: '755 KB', desc: 'Semantic search with HNSW' },
3123
+ { name: 'rag_pipeline', size: '303 KB', desc: 'RAG pipeline embeddings' },
3124
+ { name: 'agent_memory', size: '32 KB', desc: 'AI agent episodic memory' },
3125
+ { name: 'swarm_knowledge', size: '86 KB', desc: 'Multi-agent knowledge base' },
3126
+ { name: 'self_booting', size: '31 KB', desc: 'Self-booting with kernel' },
3127
+ { name: 'ebpf_accelerator', size: '153 KB', desc: 'eBPF distance accelerator' },
3128
+ { name: 'tee_attestation', size: '102 KB', desc: 'TEE attestation + witnesses' },
3129
+ { name: 'lineage_parent', size: '52 KB', desc: 'COW parent file' },
3130
+ { name: 'lineage_child', size: '26 KB', desc: 'COW child (derived)' },
3131
+ { name: 'claude_code_appliance', size: '17 KB', desc: 'Claude Code appliance' },
3132
+ { name: 'progressive_index', size: '2.5 MB', desc: 'Large-scale HNSW index' },
3133
+ ];
3134
+ let filtered = examples;
3351
3135
  if (args.filter) {
3352
3136
  const f = args.filter.toLowerCase();
3353
- examples = examples.filter(e =>
3354
- e.name.includes(f) ||
3355
- (e.description || '').toLowerCase().includes(f) ||
3356
- (e.category || '').includes(f)
3357
- );
3137
+ filtered = examples.filter(e => e.name.includes(f) || e.desc.toLowerCase().includes(f));
3358
3138
  }
3359
-
3360
- if (args.category) {
3361
- examples = examples.filter(e => e.category === args.category);
3362
- }
3363
-
3364
3139
  return { content: [{ type: 'text', text: JSON.stringify({
3365
3140
  success: true,
3366
- version: manifest.version,
3367
- total: (manifest.examples || []).length,
3368
- shown: examples.length,
3369
- examples: examples.map(e => ({
3370
- ...e,
3371
- url: `${baseUrl}/${e.name}.rvf`
3372
- })),
3373
- categories: manifest.categories || {},
3141
+ total: 45,
3142
+ shown: filtered.length,
3143
+ examples: filtered.map(e => ({ ...e, url: `${BASE_URL}/${e.name}.rvf` })),
3374
3144
  catalog: 'https://github.com/ruvnet/ruvector/tree/main/examples/rvf/output'
3375
3145
  }, null, 2) }] };
3376
3146
  }
@@ -3466,444 +3236,454 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
3466
3236
  }
3467
3237
  }
3468
3238
 
3469
- // ── Brain Tool Handlers (direct fetch to pi.ruv.io) ─────────────────
3470
- case 'brain_search':
3471
- case 'brain_share':
3472
- case 'brain_get':
3473
- case 'brain_vote':
3474
- case 'brain_list':
3475
- case 'brain_delete':
3476
- case 'brain_status':
3477
- case 'brain_drift':
3478
- case 'brain_partition':
3479
- case 'brain_transfer':
3480
- case 'brain_sync':
3481
- case 'brain_train': {
3239
+ // ── Brain Tool Handlers ─────────────────────────────────────────────
3240
+ case 'brain_search': {
3482
3241
  try {
3483
- const brainUrl = process.env.BRAIN_URL || 'https://pi.ruv.io';
3484
- const brainKey = process.env.PI;
3485
- const hdrs = { 'Content-Type': 'application/json' };
3486
- if (brainKey) hdrs['Authorization'] = `Bearer ${brainKey}`;
3487
- const subCmd = name.replace('brain_', '');
3488
- let url, fetchOpts = { headers: hdrs, signal: AbortSignal.timeout(30000) };
3489
- switch (subCmd) {
3490
- case 'search': {
3491
- const p = new URLSearchParams({ q: args.query || '' });
3492
- if (args.category) p.set('category', args.category);
3493
- if (args.limit) p.set('limit', String(args.limit));
3494
- url = `${brainUrl}/v1/memories/search?${p}`;
3495
- break;
3496
- }
3497
- case 'share': {
3498
- url = `${brainUrl}/v1/memories`;
3499
- fetchOpts.method = 'POST';
3500
- fetchOpts.body = JSON.stringify({ title: args.title, content: args.content, category: args.category, tags: args.tags ? args.tags.split(',').map(t => t.trim()) : [], code_snippet: args.code_snippet });
3501
- break;
3502
- }
3503
- case 'get': url = `${brainUrl}/v1/memories/${args.id}`; break;
3504
- case 'vote': {
3505
- url = `${brainUrl}/v1/memories/${args.id}/vote`;
3506
- fetchOpts.method = 'POST';
3507
- fetchOpts.body = JSON.stringify({ direction: args.direction });
3508
- break;
3509
- }
3510
- case 'list': {
3511
- const p = new URLSearchParams();
3512
- if (args.category) p.set('category', args.category);
3513
- p.set('limit', String(args.limit || 20));
3514
- if (args.offset) p.set('offset', String(args.offset));
3515
- if (args.sort) p.set('sort', args.sort);
3516
- if (args.tags) p.set('tags', args.tags);
3517
- url = `${brainUrl}/v1/memories/list?${p}`;
3518
- break;
3519
- }
3520
- case 'delete': {
3521
- url = `${brainUrl}/v1/memories/${args.id}`;
3522
- fetchOpts.method = 'DELETE';
3523
- break;
3524
- }
3525
- case 'status': url = `${brainUrl}/v1/status`; break;
3526
- case 'drift': {
3527
- const p = new URLSearchParams();
3528
- if (args.domain) p.set('domain', args.domain);
3529
- url = `${brainUrl}/v1/drift?${p}`;
3530
- break;
3531
- }
3532
- case 'partition': {
3533
- const p = new URLSearchParams();
3534
- if (args.domain) p.set('domain', args.domain);
3535
- if (args.min_cluster_size) p.set('min_cluster_size', String(args.min_cluster_size));
3536
- url = `${brainUrl}/v1/partition?${p}`;
3537
- break;
3538
- }
3539
- case 'transfer': {
3540
- url = `${brainUrl}/v1/transfer`;
3541
- fetchOpts.method = 'POST';
3542
- fetchOpts.body = JSON.stringify({ source_domain: args.source_domain, target_domain: args.target_domain });
3543
- break;
3544
- }
3545
- case 'sync': {
3546
- const p = new URLSearchParams();
3547
- if (args.direction) p.set('direction', args.direction);
3548
- url = `${brainUrl}/v1/lora/latest${p.toString() ? '?' + p : ''}`;
3549
- break;
3550
- }
3551
- case 'train': {
3552
- url = `${brainUrl}/v1/train`;
3553
- fetchOpts.method = 'POST';
3554
- fetchOpts.body = JSON.stringify({});
3555
- break;
3556
- }
3557
- }
3558
- const resp = await proxyFetch(url, fetchOpts);
3559
- if (!resp.ok) {
3560
- const errText = await resp.text().catch(() => resp.statusText);
3561
- return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: `${resp.status} ${errText}` }, null, 2) }], isError: true };
3562
- }
3563
- const result = (resp.status === 204 || resp.headers.get('content-length') === '0') ? {} : await resp.json();
3564
- return { content: [{ type: 'text', text: JSON.stringify({ success: true, ...result }, null, 2) }] };
3242
+ const piBrain = require('@ruvector/pi-brain');
3243
+ const PiBrainClient = piBrain.PiBrainClient || piBrain.default;
3244
+ const url = process.env.BRAIN_URL || 'https://pi.ruv.io';
3245
+ const key = process.env.PI || '';
3246
+ const client = new PiBrainClient({ url, key });
3247
+ const results = await client.search(args.query, { limit: args.limit || 10, category: args.category });
3248
+ return { content: [{ type: 'text', text: JSON.stringify({ success: true, ...results }, null, 2) }] };
3565
3249
  } catch (e) {
3250
+ if (e.code === 'MODULE_NOT_FOUND') {
3251
+ return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: 'Brain tools require @ruvector/pi-brain', hint: 'npm install @ruvector/pi-brain' }, null, 2) }] };
3252
+ }
3566
3253
  return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: e.message }, null, 2) }], isError: true };
3567
3254
  }
3568
3255
  }
3569
3256
 
3570
- // ── Brainpedia Page Tool Handlers ────────────────────────────────────
3571
- case 'brain_page_list':
3572
- case 'brain_page_get':
3573
- case 'brain_page_create':
3574
- case 'brain_page_update':
3575
- case 'brain_page_delete': {
3257
+ case 'brain_share': {
3576
3258
  try {
3577
- const brainUrl = process.env.BRAIN_URL || 'https://pi.ruv.io';
3578
- const brainKey = process.env.PI;
3579
- const hdrs = { 'Content-Type': 'application/json' };
3580
- if (brainKey) hdrs['Authorization'] = `Bearer ${brainKey}`;
3581
- let url, fetchOpts = { headers: hdrs, signal: AbortSignal.timeout(30000) };
3582
- const subCmd = name.replace('brain_page_', '');
3583
- switch (subCmd) {
3584
- case 'list': {
3585
- const p = new URLSearchParams();
3586
- if (args.limit) p.set('limit', String(args.limit));
3587
- if (args.offset) p.set('offset', String(args.offset));
3588
- if (args.status) p.set('status', args.status);
3589
- url = `${brainUrl}/v1/pages${p.toString() ? '?' + p : ''}`;
3590
- break;
3591
- }
3592
- case 'get': url = `${brainUrl}/v1/pages/${args.id}`; break;
3593
- case 'create': {
3594
- url = `${brainUrl}/v1/pages`;
3595
- fetchOpts.method = 'POST';
3596
- fetchOpts.body = JSON.stringify({ title: args.title, content: args.content, category: args.category, tags: args.tags ? args.tags.split(',').map(t => t.trim()) : [], code_snippet: args.code_snippet, evidence_links: [], embedding: [], witness_hash: '' });
3597
- break;
3598
- }
3599
- case 'update': {
3600
- url = `${brainUrl}/v1/pages/${args.page_id}/deltas`;
3601
- fetchOpts.method = 'POST';
3602
- let evidence = [];
3603
- try { if (args.evidence_links) evidence = JSON.parse(args.evidence_links); } catch {}
3604
- fetchOpts.body = JSON.stringify({ delta_type: args.delta_type || 'extension', content_diff: args.content_diff, evidence_links: evidence, witness_hash: '' });
3605
- break;
3606
- }
3607
- case 'delete': {
3608
- url = `${brainUrl}/v1/pages/${args.id}`;
3609
- fetchOpts.method = 'DELETE';
3610
- break;
3611
- }
3612
- }
3613
- const resp = await proxyFetch(url, fetchOpts);
3614
- if (!resp.ok) {
3615
- const errText = await resp.text().catch(() => resp.statusText);
3616
- return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: `${resp.status} ${errText}` }, null, 2) }], isError: true };
3617
- }
3618
- const result = (resp.status === 204 || resp.headers.get('content-length') === '0') ? {} : await resp.json();
3259
+ const piBrain = require('@ruvector/pi-brain');
3260
+ const PiBrainClient = piBrain.PiBrainClient || piBrain.default;
3261
+ const url = process.env.BRAIN_URL || 'https://pi.ruv.io';
3262
+ const key = process.env.PI || '';
3263
+ const client = new PiBrainClient({ url, key });
3264
+ const result = await client.share({ title: args.title, content: args.content, category: args.category || 'pattern', tags: args.tags });
3619
3265
  return { content: [{ type: 'text', text: JSON.stringify({ success: true, ...result }, null, 2) }] };
3620
3266
  } catch (e) {
3267
+ if (e.code === 'MODULE_NOT_FOUND') {
3268
+ return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: 'Brain tools require @ruvector/pi-brain', hint: 'npm install @ruvector/pi-brain' }, null, 2) }] };
3269
+ }
3621
3270
  return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: e.message }, null, 2) }], isError: true };
3622
3271
  }
3623
3272
  }
3624
3273
 
3625
- // ── WASM Node Tool Handlers ────────────────────────────────────────────
3626
- case 'brain_node_list':
3627
- case 'brain_node_get':
3628
- case 'brain_node_publish':
3629
- case 'brain_node_revoke': {
3274
+ case 'brain_get': {
3630
3275
  try {
3631
- const brainUrl = process.env.BRAIN_URL || 'https://pi.ruv.io';
3632
- const brainKey = process.env.PI;
3633
- const hdrs = { 'Content-Type': 'application/json' };
3634
- if (brainKey) hdrs['Authorization'] = `Bearer ${brainKey}`;
3635
- let url, fetchOpts = { headers: hdrs, signal: AbortSignal.timeout(30000) };
3636
- const subCmd = name.replace('brain_node_', '');
3637
- switch (subCmd) {
3638
- case 'list': {
3639
- const p = new URLSearchParams();
3640
- if (args.limit) p.set('limit', String(args.limit));
3641
- url = `${brainUrl}/v1/nodes${p.toString() ? '?' + p : ''}`;
3642
- break;
3643
- }
3644
- case 'get': url = `${brainUrl}/v1/nodes/${args.id}`; break;
3645
- case 'publish': {
3646
- url = `${brainUrl}/v1/nodes`;
3647
- fetchOpts.method = 'POST';
3648
- let vectors = [];
3649
- try { if (args.conformance_vectors) vectors = JSON.parse(args.conformance_vectors); } catch {}
3650
- fetchOpts.body = JSON.stringify({ name: args.name, wasm_base64: args.wasm_base64, description: args.description || '', conformance_vectors: vectors });
3651
- break;
3652
- }
3653
- case 'revoke': {
3654
- url = `${brainUrl}/v1/nodes/${args.id}/revoke`;
3655
- fetchOpts.method = 'POST';
3656
- fetchOpts.body = JSON.stringify({});
3657
- break;
3658
- }
3659
- }
3660
- const resp = await proxyFetch(url, fetchOpts);
3661
- if (!resp.ok) {
3662
- const errText = await resp.text().catch(() => resp.statusText);
3663
- return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: `${resp.status} ${errText}` }, null, 2) }], isError: true };
3664
- }
3665
- const result = (resp.status === 204 || resp.headers.get('content-length') === '0') ? {} : await resp.json();
3666
- return { content: [{ type: 'text', text: JSON.stringify({ success: true, ...(Array.isArray(result) ? { items: result, count: result.length } : result) }, null, 2) }] };
3276
+ const piBrain = require('@ruvector/pi-brain');
3277
+ const PiBrainClient = piBrain.PiBrainClient || piBrain.default;
3278
+ const url = process.env.BRAIN_URL || 'https://pi.ruv.io';
3279
+ const key = process.env.PI || '';
3280
+ const client = new PiBrainClient({ url, key });
3281
+ const result = await client.get(args.id);
3282
+ return { content: [{ type: 'text', text: JSON.stringify({ success: true, ...result }, null, 2) }] };
3667
3283
  } catch (e) {
3284
+ if (e.code === 'MODULE_NOT_FOUND') {
3285
+ return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: 'Brain tools require @ruvector/pi-brain', hint: 'npm install @ruvector/pi-brain' }, null, 2) }] };
3286
+ }
3668
3287
  return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: e.message }, null, 2) }], isError: true };
3669
3288
  }
3670
3289
  }
3671
3290
 
3672
- // ── Brain AGI Tool Handlers ────────────────────────────────────────────
3673
- case 'brain_agi_status':
3674
- case 'brain_sona_stats':
3675
- case 'brain_temporal':
3676
- case 'brain_explore':
3677
- case 'brain_midstream':
3678
- case 'brain_flags': {
3291
+ case 'brain_vote': {
3679
3292
  try {
3680
- const brainUrl = process.env.BRAIN_URL || 'https://pi.ruv.io';
3681
- const brainKey = process.env.PI;
3682
- const hdrs = { 'Content-Type': 'application/json' };
3683
- if (brainKey) hdrs['Authorization'] = `Bearer ${brainKey}`;
3684
-
3685
- const endpointMap = {
3686
- brain_agi_status: '/v1/status',
3687
- brain_sona_stats: '/v1/sona/stats',
3688
- brain_temporal: '/v1/temporal',
3689
- brain_explore: '/v1/explore',
3690
- brain_midstream: '/v1/midstream',
3691
- brain_flags: '/v1/status',
3692
- };
3693
- const endpoint = endpointMap[name];
3694
- const resp = await proxyFetch(`${brainUrl}${endpoint}`, { headers: hdrs, signal: AbortSignal.timeout(30000) });
3695
- if (!resp.ok) {
3696
- const errText = await resp.text().catch(() => resp.statusText);
3697
- return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: `${resp.status} ${errText}` }, null, 2) }], isError: true };
3698
- }
3699
- let data = await resp.json();
3700
-
3701
- // For brain_flags, extract only flag-related fields
3702
- if (name === 'brain_flags') {
3703
- const flags = {};
3704
- for (const [k, v] of Object.entries(data)) {
3705
- if (typeof v === 'boolean' || k.startsWith('rvf_') || k.endsWith('_enabled') || (k.startsWith('midstream_') && typeof v === 'boolean')) {
3706
- flags[k] = v;
3707
- }
3708
- }
3709
- data = flags;
3710
- }
3711
-
3712
- // For brain_agi_status, extract AGI-specific fields
3713
- if (name === 'brain_agi_status') {
3714
- const agiFields = {};
3715
- const agiKeys = ['sona_patterns', 'sona_trajectories', 'sona_background_ticks', 'gwt_workspace_load', 'gwt_avg_salience', 'knowledge_velocity', 'temporal_deltas', 'temporal_trend', 'meta_avg_regret', 'meta_plateau_status', 'midstream_scheduler_ticks', 'midstream_attractor_categories', 'midstream_strange_loop_version'];
3716
- for (const k of agiKeys) {
3717
- if (data[k] !== undefined) agiFields[k] = data[k];
3718
- }
3719
- data = Object.keys(agiFields).length > 0 ? agiFields : data;
3293
+ const piBrain = require('@ruvector/pi-brain');
3294
+ const PiBrainClient = piBrain.PiBrainClient || piBrain.default;
3295
+ const url = process.env.BRAIN_URL || 'https://pi.ruv.io';
3296
+ const key = process.env.PI || '';
3297
+ const client = new PiBrainClient({ url, key });
3298
+ const result = await client.vote(args.id, args.direction);
3299
+ return { content: [{ type: 'text', text: JSON.stringify({ success: true, ...result }, null, 2) }] };
3300
+ } catch (e) {
3301
+ if (e.code === 'MODULE_NOT_FOUND') {
3302
+ return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: 'Brain tools require @ruvector/pi-brain', hint: 'npm install @ruvector/pi-brain' }, null, 2) }] };
3720
3303
  }
3304
+ return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: e.message }, null, 2) }], isError: true };
3305
+ }
3306
+ }
3721
3307
 
3722
- return { content: [{ type: 'text', text: JSON.stringify({ success: true, ...data }, null, 2) }] };
3308
+ case 'brain_list': {
3309
+ try {
3310
+ const piBrain = require('@ruvector/pi-brain');
3311
+ const PiBrainClient = piBrain.PiBrainClient || piBrain.default;
3312
+ const url = process.env.BRAIN_URL || 'https://pi.ruv.io';
3313
+ const key = process.env.PI || '';
3314
+ const client = new PiBrainClient({ url, key });
3315
+ const results = await client.list({ category: args.category, limit: args.limit || 20 });
3316
+ return { content: [{ type: 'text', text: JSON.stringify({ success: true, ...results }, null, 2) }] };
3723
3317
  } catch (e) {
3318
+ if (e.code === 'MODULE_NOT_FOUND') {
3319
+ return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: 'Brain tools require @ruvector/pi-brain', hint: 'npm install @ruvector/pi-brain' }, null, 2) }] };
3320
+ }
3724
3321
  return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: e.message }, null, 2) }], isError: true };
3725
3322
  }
3726
3323
  }
3727
3324
 
3728
- // ── Midstream Tool Handlers ────────────────────────────────────────────
3729
- case 'midstream_status':
3730
- case 'midstream_attractor':
3731
- case 'midstream_scheduler': {
3325
+ case 'brain_delete': {
3732
3326
  try {
3733
- const brainUrl = process.env.BRAIN_URL || 'https://pi.ruv.io';
3734
- const brainKey = process.env.PI;
3735
- const hdrs = { 'Content-Type': 'application/json' };
3736
- if (brainKey) hdrs['Authorization'] = `Bearer ${brainKey}`;
3737
-
3738
- const resp = await proxyFetch(`${brainUrl}/v1/midstream`, { headers: hdrs, signal: AbortSignal.timeout(30000) });
3739
- if (!resp.ok) {
3740
- const errText = await resp.text().catch(() => resp.statusText);
3741
- return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: `${resp.status} ${errText}` }, null, 2) }], isError: true };
3327
+ const piBrain = require('@ruvector/pi-brain');
3328
+ const PiBrainClient = piBrain.PiBrainClient || piBrain.default;
3329
+ const url = process.env.BRAIN_URL || 'https://pi.ruv.io';
3330
+ const key = process.env.PI || '';
3331
+ const client = new PiBrainClient({ url, key });
3332
+ const result = await client.delete(args.id);
3333
+ return { content: [{ type: 'text', text: JSON.stringify({ success: true, ...result }, null, 2) }] };
3334
+ } catch (e) {
3335
+ if (e.code === 'MODULE_NOT_FOUND') {
3336
+ return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: 'Brain tools require @ruvector/pi-brain', hint: 'npm install @ruvector/pi-brain' }, null, 2) }] };
3742
3337
  }
3743
- let data = await resp.json();
3338
+ return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: e.message }, null, 2) }], isError: true };
3339
+ }
3340
+ }
3744
3341
 
3745
- if (name === 'midstream_attractor') {
3746
- data = data.attractor_categories || data.attractors || data;
3747
- if (args.category && typeof data === 'object') data = data[args.category] || { error: `Category '${args.category}' not found` };
3748
- } else if (name === 'midstream_scheduler') {
3749
- data = data.scheduler || { ticks: data.scheduler_ticks || 0 };
3342
+ case 'brain_status': {
3343
+ try {
3344
+ const piBrain = require('@ruvector/pi-brain');
3345
+ const PiBrainClient = piBrain.PiBrainClient || piBrain.default;
3346
+ const url = process.env.BRAIN_URL || 'https://pi.ruv.io';
3347
+ const key = process.env.PI || '';
3348
+ const client = new PiBrainClient({ url, key });
3349
+ const result = await client.status();
3350
+ return { content: [{ type: 'text', text: JSON.stringify({ success: true, ...result }, null, 2) }] };
3351
+ } catch (e) {
3352
+ if (e.code === 'MODULE_NOT_FOUND') {
3353
+ return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: 'Brain tools require @ruvector/pi-brain', hint: 'npm install @ruvector/pi-brain' }, null, 2) }] };
3750
3354
  }
3355
+ return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: e.message }, null, 2) }], isError: true };
3356
+ }
3357
+ }
3751
3358
 
3752
- return { content: [{ type: 'text', text: JSON.stringify({ success: true, ...data }, null, 2) }] };
3359
+ case 'brain_drift': {
3360
+ try {
3361
+ const piBrain = require('@ruvector/pi-brain');
3362
+ const PiBrainClient = piBrain.PiBrainClient || piBrain.default;
3363
+ const url = process.env.BRAIN_URL || 'https://pi.ruv.io';
3364
+ const key = process.env.PI || '';
3365
+ const client = new PiBrainClient({ url, key });
3366
+ const result = await client.drift({ domain: args.domain });
3367
+ return { content: [{ type: 'text', text: JSON.stringify({ success: true, ...result }, null, 2) }] };
3753
3368
  } catch (e) {
3369
+ if (e.code === 'MODULE_NOT_FOUND') {
3370
+ return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: 'Brain tools require @ruvector/pi-brain', hint: 'npm install @ruvector/pi-brain' }, null, 2) }] };
3371
+ }
3754
3372
  return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: e.message }, null, 2) }], isError: true };
3755
3373
  }
3756
3374
  }
3757
3375
 
3758
- case 'midstream_benchmark': {
3376
+ case 'brain_partition': {
3759
3377
  try {
3760
- const brainUrl = process.env.BRAIN_URL || 'https://pi.ruv.io';
3761
- const brainKey = process.env.PI;
3762
- const hdrs = { 'Content-Type': 'application/json' };
3763
- if (brainKey) hdrs['Authorization'] = `Bearer ${brainKey}`;
3764
- const concurrentN = Math.min(args.concurrent_count || 20, 100);
3765
-
3766
- async function timeFetch(url) {
3767
- const start = performance.now();
3768
- const resp = await proxyFetch(url, { headers: hdrs });
3769
- return { status: resp.status, elapsed: performance.now() - start };
3378
+ const piBrain = require('@ruvector/pi-brain');
3379
+ const PiBrainClient = piBrain.PiBrainClient || piBrain.default;
3380
+ const url = process.env.BRAIN_URL || 'https://pi.ruv.io';
3381
+ const key = process.env.PI || '';
3382
+ const client = new PiBrainClient({ url, key });
3383
+ const result = await client.partition({ domain: args.domain, min_cluster_size: args.min_cluster_size || 3 });
3384
+ return { content: [{ type: 'text', text: JSON.stringify({ success: true, ...result }, null, 2) }] };
3385
+ } catch (e) {
3386
+ if (e.code === 'MODULE_NOT_FOUND') {
3387
+ return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: 'Brain tools require @ruvector/pi-brain', hint: 'npm install @ruvector/pi-brain' }, null, 2) }] };
3770
3388
  }
3389
+ return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: e.message }, null, 2) }], isError: true };
3390
+ }
3391
+ }
3771
3392
 
3772
- // Sequential tests
3773
- const endpoints = [
3774
- { path: '/v1/health', label: 'health' },
3775
- { path: '/v1/status', label: 'status' },
3776
- { path: '/v1/memories/search?q=test&limit=3', label: 'search' },
3777
- { path: '/v1/midstream', label: 'midstream' },
3778
- ];
3779
-
3780
- const sequential = {};
3781
- for (const ep of endpoints) {
3782
- const times = [];
3783
- for (let i = 0; i < 3; i++) {
3784
- const r = await timeFetch(brainUrl + ep.path);
3785
- times.push(r.elapsed);
3786
- }
3787
- sequential[ep.label] = {
3788
- avg_ms: +(times.reduce((a, b) => a + b, 0) / times.length).toFixed(1),
3789
- min_ms: +Math.min(...times).toFixed(1),
3790
- max_ms: +Math.max(...times).toFixed(1)
3791
- };
3393
+ case 'brain_transfer': {
3394
+ try {
3395
+ const piBrain = require('@ruvector/pi-brain');
3396
+ const PiBrainClient = piBrain.PiBrainClient || piBrain.default;
3397
+ const url = process.env.BRAIN_URL || 'https://pi.ruv.io';
3398
+ const key = process.env.PI || '';
3399
+ const client = new PiBrainClient({ url, key });
3400
+ const result = await client.transfer(args.source, args.target);
3401
+ return { content: [{ type: 'text', text: JSON.stringify({ success: true, ...result }, null, 2) }] };
3402
+ } catch (e) {
3403
+ if (e.code === 'MODULE_NOT_FOUND') {
3404
+ return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: 'Brain tools require @ruvector/pi-brain', hint: 'npm install @ruvector/pi-brain' }, null, 2) }] };
3792
3405
  }
3406
+ return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: e.message }, null, 2) }], isError: true };
3407
+ }
3408
+ }
3793
3409
 
3794
- // Concurrent search
3795
- const promises = [];
3796
- for (let i = 0; i < concurrentN; i++) {
3797
- promises.push(timeFetch(brainUrl + '/v1/memories/search?q=test&limit=3'));
3410
+ case 'brain_sync': {
3411
+ try {
3412
+ const piBrain = require('@ruvector/pi-brain');
3413
+ const PiBrainClient = piBrain.PiBrainClient || piBrain.default;
3414
+ const url = process.env.BRAIN_URL || 'https://pi.ruv.io';
3415
+ const key = process.env.PI || '';
3416
+ const client = new PiBrainClient({ url, key });
3417
+ const result = await client.sync({ direction: args.direction || 'both' });
3418
+ return { content: [{ type: 'text', text: JSON.stringify({ success: true, ...result }, null, 2) }] };
3419
+ } catch (e) {
3420
+ if (e.code === 'MODULE_NOT_FOUND') {
3421
+ return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: 'Brain tools require @ruvector/pi-brain', hint: 'npm install @ruvector/pi-brain' }, null, 2) }] };
3798
3422
  }
3799
- const results = await Promise.all(promises);
3800
- const sorted = results.map(r => r.elapsed).sort((a, b) => a - b);
3801
- const pct = (p) => +(sorted[Math.max(0, Math.ceil(sorted.length * p / 100) - 1)]).toFixed(1);
3423
+ return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: e.message }, null, 2) }], isError: true };
3424
+ }
3425
+ }
3802
3426
 
3803
- return { content: [{ type: 'text', text: JSON.stringify({
3804
- success: true,
3805
- sequential,
3806
- concurrent: { count: concurrentN, p50_ms: pct(50), p90_ms: pct(90), p99_ms: pct(99) }
3807
- }, null, 2) }] };
3427
+ // ── Edge Tool Handlers ──────────────────────────────────────────────
3428
+ case 'edge_status': {
3429
+ try {
3430
+ const res = await fetch('https://edge-net-genesis-875130704813.us-central1.run.app/api/status');
3431
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
3432
+ const data = await res.json();
3433
+ return { content: [{ type: 'text', text: JSON.stringify({ success: true, ...data }, null, 2) }] };
3808
3434
  } catch (e) {
3809
3435
  return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: e.message }, null, 2) }], isError: true };
3810
3436
  }
3811
3437
  }
3812
3438
 
3813
- case 'midstream_search': {
3439
+ case 'edge_join': {
3814
3440
  try {
3815
- const brainUrl = process.env.BRAIN_URL || 'https://pi.ruv.io';
3816
- const brainKey = process.env.PI;
3817
- const hdrs = { 'Content-Type': 'application/json' };
3818
- if (brainKey) hdrs['Authorization'] = `Bearer ${brainKey}`;
3819
- const limit = Math.min(Math.max(parseInt(args.limit) || 10, 1), 100);
3820
- const q = encodeURIComponent(args.query);
3821
- const resp = await proxyFetch(`${brainUrl}/v1/memories/search?q=${q}&limit=${limit}`, { headers: hdrs, signal: AbortSignal.timeout(30000) });
3822
- if (!resp.ok) {
3823
- const errText = await resp.text().catch(() => resp.statusText);
3824
- return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: `${resp.status} ${errText}` }, null, 2) }], isError: true };
3825
- }
3826
- const data = await resp.json();
3827
- return { content: [{ type: 'text', text: JSON.stringify({ success: true, results: data, count: Array.isArray(data) ? data.length : 0 }, null, 2) }] };
3441
+ const key = args.key || process.env.PI || '';
3442
+ const res = await fetch('https://edge-net-genesis-875130704813.us-central1.run.app/api/join', {
3443
+ method: 'POST',
3444
+ headers: { 'Content-Type': 'application/json' },
3445
+ body: JSON.stringify({ contribution: args.contribution || 0.3, key })
3446
+ });
3447
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
3448
+ const data = await res.json();
3449
+ return { content: [{ type: 'text', text: JSON.stringify({ success: true, ...data }, null, 2) }] };
3828
3450
  } catch (e) {
3829
3451
  return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: e.message }, null, 2) }], isError: true };
3830
3452
  }
3831
3453
  }
3832
3454
 
3833
- case 'midstream_health': {
3455
+ case 'edge_balance': {
3834
3456
  try {
3835
- const brainUrl = process.env.BRAIN_URL || 'https://pi.ruv.io';
3836
- const brainKey = process.env.PI;
3837
- const hdrs = { 'Content-Type': 'application/json' };
3838
- if (brainKey) hdrs['Authorization'] = `Bearer ${brainKey}`;
3839
-
3840
- const [healthResp, midResp] = await Promise.all([
3841
- proxyFetch(`${brainUrl}/v1/health`, { headers: hdrs, signal: AbortSignal.timeout(15000) }).then(r => r.json()).catch(e => ({ error: e.message })),
3842
- proxyFetch(`${brainUrl}/v1/midstream`, { headers: hdrs, signal: AbortSignal.timeout(15000) }).then(r => r.json()).catch(e => ({ error: e.message })),
3843
- ]);
3844
-
3845
- return { content: [{ type: 'text', text: JSON.stringify({
3846
- success: true,
3847
- health: healthResp,
3848
- midstream: midResp
3849
- }, null, 2) }] };
3457
+ const key = args.key || process.env.PI || '';
3458
+ const res = await fetch(`https://edge-net-genesis-875130704813.us-central1.run.app/api/balance?key=${encodeURIComponent(key)}`);
3459
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
3460
+ const data = await res.json();
3461
+ return { content: [{ type: 'text', text: JSON.stringify({ success: true, ...data }, null, 2) }] };
3850
3462
  } catch (e) {
3851
3463
  return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: e.message }, null, 2) }], isError: true };
3852
3464
  }
3853
3465
  }
3854
3466
 
3855
- // ── Edge Tool Handlers ───────────────────────────────────────────────
3856
- case 'edge_status':
3857
- case 'edge_join':
3858
- case 'edge_balance':
3859
3467
  case 'edge_tasks': {
3860
3468
  try {
3861
- const genesisUrl = process.env.EDGE_GENESIS_URL || 'https://edge-net-genesis-875130704813.us-central1.run.app';
3862
- const subCmd = name.replace('edge_', '');
3863
- let endpoint, method = 'GET', body;
3864
- switch (subCmd) {
3865
- case 'status': endpoint = '/status'; break;
3866
- case 'join': endpoint = '/join'; method = 'POST'; body = JSON.stringify({ contribution: args.contribution || 0.3, pi_key: process.env.PI }); break;
3867
- case 'balance': { const ps = process.env.PI ? require('crypto').createHash('shake256', { outputLength: 16 }).update(process.env.PI).digest('hex') : 'anonymous'; endpoint = `/balance/${ps}`; break; }
3868
- case 'tasks': endpoint = `/tasks?limit=${args.limit || 20}`; break;
3869
- }
3870
- const resp = await proxyFetch(`${genesisUrl}${endpoint}`, {
3871
- method,
3872
- headers: { 'Content-Type': 'application/json', ...(process.env.PI ? { 'Authorization': `Bearer ${process.env.PI}` } : {}) },
3873
- ...(body ? { body } : {})
3874
- });
3875
- if (!resp.ok) {
3876
- const errText = await resp.text().catch(() => resp.statusText);
3877
- return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: `${resp.status} ${errText}` }, null, 2) }], isError: true };
3878
- }
3879
- const data = await resp.json();
3469
+ const res = await fetch('https://edge-net-genesis-875130704813.us-central1.run.app/api/tasks');
3470
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
3471
+ const data = await res.json();
3880
3472
  return { content: [{ type: 'text', text: JSON.stringify({ success: true, ...data }, null, 2) }] };
3881
3473
  } catch (e) {
3882
3474
  return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: e.message }, null, 2) }], isError: true };
3883
3475
  }
3884
3476
  }
3885
3477
 
3886
- // ── Identity Tool Handlers ───────────────────────────────────────────
3478
+ // ── Identity Tool Handlers ──────────────────────────────────────────
3887
3479
  case 'identity_generate': {
3888
3480
  const crypto = require('crypto');
3889
3481
  const key = crypto.randomBytes(32).toString('hex');
3890
- const hash = crypto.createHash('shake256', { outputLength: 16 });
3891
- hash.update(key);
3892
- const pseudonym = hash.digest('hex');
3893
- return { content: [{ type: 'text', text: JSON.stringify({ success: true, pi_key: key, pseudonym, warning: 'Store this key securely. Set PI env var to use it.' }, null, 2) }] };
3482
+ const pseudonym = crypto.createHash('shake256', { outputLength: 16 }).update(key).digest('hex');
3483
+ const mcpToken = crypto.createHmac('sha256', key).update('mcp').digest('hex').slice(0, 32);
3484
+ return { content: [{ type: 'text', text: JSON.stringify({ success: true, key, pseudonym, mcp_token: mcpToken, instructions: 'Set PI env var: export PI=' + key }, null, 2) }] };
3894
3485
  }
3895
3486
 
3896
3487
  case 'identity_show': {
3897
- const piKey = process.env.PI;
3898
- if (!piKey) {
3899
- return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: 'No PI environment variable set. Run identity_generate first.' }, null, 2) }], isError: true };
3900
- }
3901
3488
  const crypto = require('crypto');
3902
- const hash = crypto.createHash('shake256', { outputLength: 16 });
3903
- hash.update(piKey);
3904
- const pseudonym = hash.digest('hex');
3905
- const mcpToken = crypto.createHmac('sha256', piKey).update('mcp').digest('hex').slice(0, 32);
3906
- return { content: [{ type: 'text', text: JSON.stringify({ success: true, pseudonym, mcp_token: mcpToken, key_prefix: piKey.slice(0, 8) + '...' }, null, 2) }] };
3489
+ const key = args.key || process.env.PI || '';
3490
+ if (!key) {
3491
+ return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: 'No PI key found. Set PI env var or pass key argument' }, null, 2) }] };
3492
+ }
3493
+ const pseudonym = crypto.createHash('shake256', { outputLength: 16 }).update(key).digest('hex');
3494
+ const mcpToken = crypto.createHmac('sha256', key).update('mcp').digest('hex').slice(0, 32);
3495
+ return { content: [{ type: 'text', text: JSON.stringify({ success: true, pseudonym, mcp_token: mcpToken, key_prefix: key.slice(0, 8) + '...' }, null, 2) }] };
3496
+ }
3497
+
3498
+ // ── Decompiler Tool Handlers ─────────────────────────────────────────
3499
+ case 'decompile_package': {
3500
+ const decompiler = require('../src/decompiler/index.js');
3501
+ const result = await decompiler.decompilePackage(
3502
+ args.package,
3503
+ args.version || undefined,
3504
+ { minConfidence: args.min_confidence || 0.3 }
3505
+ );
3506
+ return {
3507
+ content: [{
3508
+ type: 'text',
3509
+ text: JSON.stringify({
3510
+ success: true,
3511
+ packageInfo: result.packageInfo,
3512
+ modules: result.modules.map(m => ({
3513
+ name: m.name, fragments: m.fragments, confidence: m.confidence,
3514
+ contentPreview: m.content.slice(0, 500) + (m.content.length > 500 ? '...' : ''),
3515
+ })),
3516
+ metrics: result.metrics,
3517
+ witness_root: result.witness ? result.witness.root : null,
3518
+ }, null, 2)
3519
+ }]
3520
+ };
3521
+ }
3522
+
3523
+ case 'decompile_file': {
3524
+ const decompiler = require('../src/decompiler/index.js');
3525
+ const safePath = validateRvfPath(args.path);
3526
+ const result = decompiler.decompileFile(safePath, {
3527
+ minConfidence: args.min_confidence || 0.3
3528
+ });
3529
+ return {
3530
+ content: [{
3531
+ type: 'text',
3532
+ text: JSON.stringify({
3533
+ success: true,
3534
+ filePath: result.filePath,
3535
+ modules: result.modules.map(m => ({
3536
+ name: m.name, fragments: m.fragments, confidence: m.confidence,
3537
+ contentPreview: m.content.slice(0, 500) + (m.content.length > 500 ? '...' : ''),
3538
+ })),
3539
+ metrics: result.metrics,
3540
+ witness_root: result.witness ? result.witness.root : null,
3541
+ }, null, 2)
3542
+ }]
3543
+ };
3544
+ }
3545
+
3546
+ case 'decompile_url': {
3547
+ const decompiler = require('../src/decompiler/index.js');
3548
+ const urlStr = args.url;
3549
+ // Basic URL validation
3550
+ if (!urlStr.startsWith('http://') && !urlStr.startsWith('https://')) {
3551
+ throw new Error('URL must start with http:// or https://');
3552
+ }
3553
+ const result = await decompiler.decompileUrl(urlStr, {
3554
+ minConfidence: args.min_confidence || 0.3
3555
+ });
3556
+ return {
3557
+ content: [{
3558
+ type: 'text',
3559
+ text: JSON.stringify({
3560
+ success: true,
3561
+ url: result.url,
3562
+ modules: result.modules.map(m => ({
3563
+ name: m.name, fragments: m.fragments, confidence: m.confidence,
3564
+ contentPreview: m.content.slice(0, 500) + (m.content.length > 500 ? '...' : ''),
3565
+ })),
3566
+ metrics: result.metrics,
3567
+ witness_root: result.witness ? result.witness.root : null,
3568
+ }, null, 2)
3569
+ }]
3570
+ };
3571
+ }
3572
+
3573
+ case 'decompile_search': {
3574
+ const decompiler = require('../src/decompiler/index.js');
3575
+ let result;
3576
+ if (args.path) {
3577
+ const safePath = validateRvfPath(args.path);
3578
+ result = decompiler.decompileFile(safePath);
3579
+ } else if (args.package) {
3580
+ result = await decompiler.decompilePackage(args.package, args.version || undefined);
3581
+ } else {
3582
+ throw new Error('Either "package" or "path" must be provided');
3583
+ }
3584
+
3585
+ const query = args.query;
3586
+ let regex;
3587
+ try { regex = new RegExp(query, 'gi'); } catch { regex = new RegExp(query.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'gi'); }
3588
+
3589
+ const matches = [];
3590
+ for (const mod of result.modules) {
3591
+ const lines = mod.content.split('\n');
3592
+ for (let i = 0; i < lines.length; i++) {
3593
+ if (regex.test(lines[i])) {
3594
+ matches.push({
3595
+ module: mod.name,
3596
+ line: i + 1,
3597
+ content: lines[i].trim().slice(0, 200),
3598
+ });
3599
+ regex.lastIndex = 0;
3600
+ }
3601
+ if (matches.length >= 50) break;
3602
+ }
3603
+ if (matches.length >= 50) break;
3604
+ }
3605
+
3606
+ return {
3607
+ content: [{
3608
+ type: 'text',
3609
+ text: JSON.stringify({
3610
+ success: true,
3611
+ query,
3612
+ total_matches: matches.length,
3613
+ matches,
3614
+ }, null, 2)
3615
+ }]
3616
+ };
3617
+ }
3618
+
3619
+ case 'decompile_diff': {
3620
+ const decompiler = require('../src/decompiler/index.js');
3621
+ const [resultA, resultB] = await Promise.all([
3622
+ decompiler.decompilePackage(args.package, args.version_a),
3623
+ decompiler.decompilePackage(args.package, args.version_b),
3624
+ ]);
3625
+
3626
+ const namesA = new Set(resultA.modules.map(m => m.name));
3627
+ const namesB = new Set(resultB.modules.map(m => m.name));
3628
+ const added = [...namesB].filter(n => !namesA.has(n));
3629
+ const removed = [...namesA].filter(n => !namesB.has(n));
3630
+ const common = [...namesA].filter(n => namesB.has(n));
3631
+
3632
+ // Compare declarations in common modules
3633
+ const changedDeclarations = [];
3634
+ for (const name of common) {
3635
+ const modA = resultA.modules.find(m => m.name === name);
3636
+ const modB = resultB.modules.find(m => m.name === name);
3637
+ if (modA && modB && modA.content !== modB.content) {
3638
+ changedDeclarations.push({
3639
+ module: name,
3640
+ sizeChange: modB.content.length - modA.content.length,
3641
+ fragmentsA: modA.fragments,
3642
+ fragmentsB: modB.fragments,
3643
+ });
3644
+ }
3645
+ }
3646
+
3647
+ return {
3648
+ content: [{
3649
+ type: 'text',
3650
+ text: JSON.stringify({
3651
+ success: true,
3652
+ package: args.package,
3653
+ version_a: args.version_a,
3654
+ version_b: args.version_b,
3655
+ added_modules: added,
3656
+ removed_modules: removed,
3657
+ common_modules: common.length,
3658
+ changed_declarations: changedDeclarations,
3659
+ metrics_a: resultA.metrics.source,
3660
+ metrics_b: resultB.metrics.source,
3661
+ }, null, 2)
3662
+ }]
3663
+ };
3664
+ }
3665
+
3666
+ case 'decompile_witness': {
3667
+ const decompiler = require('../src/decompiler/index.js');
3668
+ const witnessPath = validateRvfPath(args.witness_path);
3669
+ const witnessData = JSON.parse(fs.readFileSync(witnessPath, 'utf-8'));
3670
+
3671
+ let sourceContent = undefined;
3672
+ if (args.source_path) {
3673
+ const sourcePath = validateRvfPath(args.source_path);
3674
+ sourceContent = fs.readFileSync(sourcePath, 'utf-8');
3675
+ }
3676
+
3677
+ const verification = decompiler.verifyWitnessChain(witnessData, sourceContent);
3678
+ return {
3679
+ content: [{
3680
+ type: 'text',
3681
+ text: JSON.stringify({
3682
+ success: true,
3683
+ ...verification,
3684
+ }, null, 2)
3685
+ }]
3686
+ };
3907
3687
  }
3908
3688
 
3909
3689
  default:
@@ -3994,173 +3774,9 @@ server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
3994
3774
 
3995
3775
  // Start server
3996
3776
  async function main() {
3997
- const transportType = process.env.MCP_TRANSPORT || 'stdio';
3998
-
3999
- if (transportType === 'sse') {
4000
- const http = require('http');
4001
- const crypto = require('crypto');
4002
- const port = parseInt(process.env.MCP_PORT || '8080', 10);
4003
- const host = process.env.MCP_HOST || '0.0.0.0';
4004
-
4005
- // SSE MCP Transport Implementation
4006
- // MCP over SSE uses:
4007
- // GET /sse - SSE stream for server->client messages
4008
- // POST /message - client->server JSON-RPC messages
4009
-
4010
- const sessions = new Map();
4011
-
4012
- const httpServer = http.createServer(async (req, res) => {
4013
- // CORS headers
4014
- res.setHeader('Access-Control-Allow-Origin', '*');
4015
- res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
4016
- res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
4017
-
4018
- if (req.method === 'OPTIONS') {
4019
- res.writeHead(204);
4020
- res.end();
4021
- return;
4022
- }
4023
-
4024
- const url = new URL(req.url, `http://${req.headers.host}`);
4025
-
4026
- if (req.method === 'GET' && url.pathname === '/sse') {
4027
- // SSE endpoint - establish persistent connection
4028
- const sessionId = crypto.randomUUID();
4029
-
4030
- res.writeHead(200, {
4031
- 'Content-Type': 'text/event-stream',
4032
- 'Cache-Control': 'no-cache',
4033
- 'Connection': 'keep-alive',
4034
- });
4035
-
4036
- // Send endpoint event so client knows where to POST
4037
- const displayHost = host === '0.0.0.0' ? 'localhost' : host;
4038
- const messageUrl = `http://${displayHost}:${port}/message?sessionId=${sessionId}`;
4039
- res.write(`event: endpoint\ndata: ${messageUrl}\n\n`);
4040
-
4041
- // Store session
4042
- sessions.set(sessionId, {
4043
- res,
4044
- messageQueue: [],
4045
- });
4046
-
4047
- // Create a custom transport for this session
4048
- const sessionTransport = {
4049
- _onMessage: null,
4050
- _onClose: null,
4051
- _onError: null,
4052
- _started: false,
4053
-
4054
- async start() {
4055
- this._started = true;
4056
- },
4057
-
4058
- async close() {
4059
- sessions.delete(sessionId);
4060
- if (!res.writableEnded) {
4061
- res.end();
4062
- }
4063
- },
4064
-
4065
- async send(message) {
4066
- if (!res.writableEnded) {
4067
- res.write(`event: message\ndata: ${JSON.stringify(message)}\n\n`);
4068
- }
4069
- },
4070
-
4071
- set onmessage(handler) { this._onMessage = handler; },
4072
- get onmessage() { return this._onMessage; },
4073
- set onclose(handler) { this._onClose = handler; },
4074
- get onclose() { return this._onClose; },
4075
- set onerror(handler) { this._onError = handler; },
4076
- get onerror() { return this._onError; },
4077
- };
4078
-
4079
- sessions.get(sessionId).transport = sessionTransport;
4080
-
4081
- // Connect server to this transport
4082
- await server.connect(sessionTransport);
4083
-
4084
- // Process any queued messages
4085
- const session = sessions.get(sessionId);
4086
- if (session) {
4087
- for (const msg of session.messageQueue) {
4088
- if (sessionTransport._onMessage) {
4089
- sessionTransport._onMessage(msg);
4090
- }
4091
- }
4092
- session.messageQueue = [];
4093
- }
4094
-
4095
- // Handle disconnect
4096
- req.on('close', () => {
4097
- sessions.delete(sessionId);
4098
- if (sessionTransport._onClose) {
4099
- sessionTransport._onClose();
4100
- }
4101
- });
4102
-
4103
- } else if (req.method === 'POST' && url.pathname === '/message') {
4104
- // Message endpoint - receive client JSON-RPC messages
4105
- const sessionId = url.searchParams.get('sessionId');
4106
- const session = sessions.get(sessionId);
4107
-
4108
- if (!session) {
4109
- res.writeHead(404, { 'Content-Type': 'application/json' });
4110
- res.end(JSON.stringify({ error: 'Session not found' }));
4111
- return;
4112
- }
4113
-
4114
- let body = '';
4115
- req.on('data', chunk => { body += chunk; });
4116
- req.on('end', () => {
4117
- try {
4118
- const message = JSON.parse(body);
4119
-
4120
- if (session.transport && session.transport._onMessage) {
4121
- session.transport._onMessage(message);
4122
- } else {
4123
- session.messageQueue.push(message);
4124
- }
4125
-
4126
- res.writeHead(202, { 'Content-Type': 'application/json' });
4127
- res.end(JSON.stringify({ status: 'accepted' }));
4128
- } catch (e) {
4129
- res.writeHead(400, { 'Content-Type': 'application/json' });
4130
- res.end(JSON.stringify({ error: 'Invalid JSON' }));
4131
- }
4132
- });
4133
-
4134
- } else if (req.method === 'GET' && url.pathname === '/health') {
4135
- res.writeHead(200, { 'Content-Type': 'application/json' });
4136
- res.end(JSON.stringify({
4137
- status: 'ok',
4138
- transport: 'sse',
4139
- sessions: sessions.size,
4140
- tools: 91,
4141
- version: '0.2.13'
4142
- }));
4143
-
4144
- } else {
4145
- res.writeHead(404, { 'Content-Type': 'application/json' });
4146
- res.end(JSON.stringify({ error: 'Not found. Use GET /sse for SSE stream, POST /message for JSON-RPC, GET /health for status.' }));
4147
- }
4148
- });
4149
-
4150
- httpServer.listen(port, host, () => {
4151
- const displayHost = host === '0.0.0.0' ? 'localhost' : host;
4152
- console.error(`RuVector MCP server running on SSE at http://${host}:${port}`);
4153
- console.error(` SSE endpoint: http://${displayHost}:${port}/sse`);
4154
- console.error(` Message endpoint: http://${displayHost}:${port}/message`);
4155
- console.error(` Health check: http://${displayHost}:${port}/health`);
4156
- });
4157
-
4158
- } else {
4159
- // Default: stdio transport
4160
- const transport = new StdioServerTransport();
4161
- await server.connect(transport);
4162
- console.error('RuVector MCP server running on stdio');
4163
- }
3777
+ const transport = new StdioServerTransport();
3778
+ await server.connect(transport);
3779
+ console.error('RuVector MCP server running on stdio');
4164
3780
  }
4165
3781
 
4166
3782
  main().catch(console.error);