squeezr-ai 1.17.5 → 1.17.7

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/README.md CHANGED
@@ -174,7 +174,7 @@ Squeezr uses cheap/free models for AI compression (the deterministic layer is pu
174
174
  ## CLI commands
175
175
 
176
176
  ```bash
177
- squeezr setup # configure env vars, auto-start, CA trust
177
+ squeezr setup # configure env vars, auto-start, CA trust, install MCP server
178
178
  squeezr start # start the proxy (auto-restarts if version mismatch after update)
179
179
  squeezr update # kill old processes, install latest from npm, restart
180
180
  squeezr stop # stop the proxy
@@ -184,10 +184,28 @@ squeezr config # print current config
184
184
  squeezr ports # change HTTP and MITM proxy ports
185
185
  squeezr gain # estimate token savings for a directory
186
186
  squeezr discover # detect which AI CLIs are installed
187
+ squeezr mcp install # register MCP server in Claude Code, Cursor, Windsurf, Cline
188
+ squeezr mcp uninstall # remove MCP server registration
187
189
  squeezr uninstall # remove Squeezr completely (env vars, CA, auto-start, logs)
188
190
  squeezr version # print version
189
191
  ```
190
192
 
193
+ ## MCP server
194
+
195
+ Squeezr ships with a built-in MCP server (`squeezr-mcp`) that gives any MCP-capable AI CLI real-time awareness of Squeezr's state and control over it.
196
+
197
+ **Installed automatically** by `squeezr setup` into Claude Code, Cursor, Windsurf, and Cline.
198
+
199
+ Available MCP tools:
200
+
201
+ | Tool | Description |
202
+ |---|---|
203
+ | `squeezr_status` | Is proxy running? Version, port, uptime, mode |
204
+ | `squeezr_stats` | Token savings, compression %, cost saved, per-tool breakdown |
205
+ | `squeezr_set_mode` | Change compression mode instantly (soft / normal / aggressive / critical) |
206
+ | `squeezr_config` | Current thresholds, keepRecent, cache sizes |
207
+ | `squeezr_habits` | Detect wasteful patterns this session (duplicate reads, high Bash count, cache efficiency) |
208
+
191
209
  ## Requirements
192
210
 
193
211
  - Node.js 18+ (compatible with Node.js 24)
package/bin/squeezr.js CHANGED
@@ -199,10 +199,10 @@ Usage:
199
199
  squeezr discover Show pattern coverage report (proxy must be running)
200
200
  squeezr status Check if proxy is running
201
201
  squeezr config Print config file path and current settings
202
+ squeezr mcp install Register Squeezr MCP server in Claude Code, Cursor, Windsurf & Cline
203
+ squeezr mcp uninstall Remove Squeezr MCP registration
202
204
  squeezr ports Change HTTP and MITM proxy ports
203
205
  squeezr tunnel Expose proxy via Cloudflare Tunnel for Cursor IDE
204
- squeezr cursor Start Cursor subscription MITM proxy (no BYOK needed)
205
- squeezr cursor stop Stop Cursor proxy and clean up system proxy settings
206
206
  squeezr update Kill old processes, install latest from npm, restart
207
207
  squeezr uninstall Remove Squeezr completely (env vars, CA, auto-start, logs)
208
208
  squeezr version Print version
@@ -393,6 +393,100 @@ function showConfig() {
393
393
  }
394
394
  }
395
395
 
396
+
397
+ // ── squeezr mcp ───────────────────────────────────────────────────────────────
398
+
399
+ async function mcpInstall() {
400
+ const mcpServerPath = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', 'dist', 'mcp.js')
401
+ const entry = {
402
+ type: 'stdio',
403
+ command: 'node',
404
+ args: [mcpServerPath],
405
+ }
406
+
407
+ const targets = [
408
+ {
409
+ name: 'Claude Code',
410
+ file: path.join(os.homedir(), '.claude.json'),
411
+ key: 'mcpServers',
412
+ },
413
+ {
414
+ name: 'Cursor',
415
+ file: path.join(os.homedir(), '.cursor', 'mcp.json'),
416
+ key: 'mcpServers',
417
+ },
418
+ {
419
+ name: 'Windsurf',
420
+ file: path.join(os.homedir(), '.codeium', 'windsurf', 'mcp_config.json'),
421
+ key: 'mcpServers',
422
+ },
423
+ {
424
+ name: 'Cline / Roo-Cline',
425
+ file: path.join(os.homedir(), '.vscode', 'extensions', 'mcp_settings.json'),
426
+ key: 'mcpServers',
427
+ },
428
+ ]
429
+
430
+ let installed = 0
431
+
432
+ for (const target of targets) {
433
+ try {
434
+ // Only install into configs that already exist (user has that tool)
435
+ if (!fs.existsSync(target.file) && target.name !== 'Claude Code') continue
436
+
437
+ let cfg = {}
438
+ if (fs.existsSync(target.file)) {
439
+ try { cfg = JSON.parse(fs.readFileSync(target.file, 'utf-8')) } catch { cfg = {} }
440
+ }
441
+ cfg[target.key] = cfg[target.key] || {}
442
+ cfg[target.key].squeezr = entry
443
+
444
+ const dir = path.dirname(target.file)
445
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true })
446
+ fs.writeFileSync(target.file, JSON.stringify(cfg, null, 2))
447
+ console.log()
448
+ console.log(' ok ' + target.name + ': ' + target.file)
449
+ } catch (e) {
450
+ console.warn()
451
+ console.warn(' warn ' + target.name + ': ' + (e.message || e))
452
+ }
453
+
454
+ console.log()
455
+ console.log('MCP server registered in ' + installed + ' client(s).')
456
+ console.log('Server binary: ' + mcpServerPath)
457
+ console.log('')
458
+ console.log('Available tools in Claude/Codex/Cursor:')
459
+ console.log(' squeezr_status — Check if Squeezr is running')
460
+ console.log(' squeezr_stats — Real-time token savings')
461
+ console.log(' squeezr_set_mode — Change compression aggressiveness')
462
+ console.log(' squeezr_config — Current configuration')
463
+ console.log(' squeezr_habits — Wasteful pattern report')
464
+ }
465
+
466
+ async function mcpUninstall() {
467
+ const files = [
468
+ path.join(os.homedir(), '.claude.json'),
469
+ path.join(os.homedir(), '.cursor', 'mcp.json'),
470
+ path.join(os.homedir(), '.codeium', 'windsurf', 'mcp_config.json'),
471
+ path.join(os.homedir(), '.vscode', 'extensions', 'mcp_settings.json'),
472
+ ]
473
+ let removed = 0
474
+ for (const file of files) {
475
+ if (!fs.existsSync(file)) continue
476
+ try {
477
+ const cfg = JSON.parse(fs.readFileSync(file, 'utf-8'))
478
+ if (cfg.mcpServers?.squeezr) {
479
+ delete cfg.mcpServers.squeezr
480
+ fs.writeFileSync(file, JSON.stringify(cfg, null, 2))
481
+ console.log()
482
+ removed++
483
+ }
484
+ } catch { /* ignore */ }
485
+ }
486
+ if (removed === 0) console.log('Squeezr MCP not found in any config.')
487
+ else console.log()
488
+ }
489
+
396
490
  // ── squeezr ports ─────────────────────────────────────────────────────────────
397
491
 
398
492
  async function configurePorts() {
@@ -1256,162 +1350,6 @@ async function startTunnel() {
1256
1350
  process.on('SIGTERM', () => { child.kill(); process.exit(0) })
1257
1351
  }
1258
1352
 
1259
- // ── squeezr cursor ───────────────────────────────────────────────────────────
1260
-
1261
- async function startCursorProxy() {
1262
- const port = getPort()
1263
- const mitmPort = getMitmPort(port)
1264
-
1265
- // Verify main proxy is running first
1266
- const running = await new Promise(resolve => {
1267
- const req = http.get(`http://localhost:${port}/squeezr/health`, res => {
1268
- resolve(res.statusCode === 200)
1269
- })
1270
- req.on('error', () => resolve(false))
1271
- req.setTimeout(2000, () => { req.destroy(); resolve(false) })
1272
- })
1273
-
1274
- if (!running) {
1275
- console.error(`Squeezr proxy is not running on port ${port}.`)
1276
- console.error('Start it first: squeezr start')
1277
- process.exit(1)
1278
- }
1279
-
1280
- // Verify CA exists
1281
- const caDir = path.join(os.homedir(), '.squeezr', 'mitm-ca')
1282
- const caCertPath = path.join(caDir, 'ca.crt')
1283
- if (!fs.existsSync(caCertPath)) {
1284
- console.error('MITM CA certificate not found. Run `squeezr setup` first.')
1285
- process.exit(1)
1286
- }
1287
-
1288
- console.log('Starting Cursor MITM proxy...')
1289
-
1290
- const distPath = path.join(ROOT, 'dist', 'cursorMitm.js')
1291
- if (!fs.existsSync(distPath)) {
1292
- console.error(`Error: ${distPath} not found. Run 'npm run build' first.`)
1293
- process.exit(1)
1294
- }
1295
-
1296
- const distUrl = process.platform === 'win32' ? 'file:///' + distPath.replace(/\\/g, '/') : distPath
1297
- const { startCursorMitm, getCursorMitmPort, getCursorStats } = await import(distUrl)
1298
-
1299
- try {
1300
- await startCursorMitm()
1301
- } catch (err) {
1302
- console.error('Failed to start Cursor proxy:', err.message)
1303
- process.exit(1)
1304
- }
1305
-
1306
- const actualPort = getCursorMitmPort()
1307
- const proxyConfigured = configureSystemProxy(actualPort)
1308
-
1309
- console.log('')
1310
- console.log('╔══════════════════════════════════════════════════════════════════╗')
1311
- console.log(`║ Cursor MITM proxy active on port ${actualPort} ║`)
1312
- console.log('╠══════════════════════════════════════════════════════════════════╣')
1313
- console.log('║ ║')
1314
- console.log('║ Intercepting: api2.cursor.sh (chat, agent, composer) ║')
1315
- console.log('║ Compressing: conversation context via cursor-small ║')
1316
- console.log('║ Everything else: transparent pass-through ║')
1317
- console.log('║ ║')
1318
- if (proxyConfigured) {
1319
- console.log('║ System proxy configured automatically. ║')
1320
- console.log('║ Cursor will route through Squeezr on next request. ║')
1321
- } else {
1322
- console.log('║ ⚠ Could not set system proxy automatically. ║')
1323
- console.log('║ Set it manually: ║')
1324
- if (process.platform === 'win32') {
1325
- console.log(`║ Settings > Network > Proxy > Manual: 127.0.0.1:${actualPort} ║`)
1326
- } else if (process.platform === 'darwin') {
1327
- console.log(`║ networksetup -setsecurewebproxy Wi-Fi 127.0.0.1 ${actualPort} ║`)
1328
- } else {
1329
- console.log(`║ export HTTPS_PROXY=http://127.0.0.1:${actualPort} ║`)
1330
- }
1331
- }
1332
- console.log('║ ║')
1333
- console.log('║ Press Ctrl+C to stop and clean up proxy settings ║')
1334
- console.log('╚══════════════════════════════════════════════════════════════════╝')
1335
- console.log('')
1336
-
1337
- const statsInterval = setInterval(() => {
1338
- const s = getCursorStats()
1339
- if (s.requests > 0) {
1340
- console.log(`[squeezr/cursor] Stats: ${s.requests} requests, ${s.compressed} compressed, -${s.charsSaved.toLocaleString()} chars saved`)
1341
- }
1342
- }, 30_000)
1343
-
1344
- const cleanup = () => {
1345
- clearInterval(statsInterval)
1346
- console.log('\nStopping Cursor proxy...')
1347
- cleanSystemProxy()
1348
- const s = getCursorStats()
1349
- console.log(`Session: ${s.requests} requests, ${s.compressed} compressed, -${s.charsSaved.toLocaleString()} chars saved`)
1350
- process.exit(0)
1351
- }
1352
-
1353
- process.on('SIGINT', cleanup)
1354
- process.on('SIGTERM', cleanup)
1355
- }
1356
-
1357
- function configureSystemProxy(port) {
1358
- try {
1359
- if (process.platform === 'win32') {
1360
- execSync(`reg add "HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings" /v ProxyEnable /t REG_DWORD /d 1 /f`, { stdio: 'pipe' })
1361
- execSync(`reg add "HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings" /v ProxyServer /t REG_SZ /d "127.0.0.1:${port}" /f`, { stdio: 'pipe' })
1362
- execSync(`reg add "HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings" /v ProxyOverride /t REG_SZ /d "<local>;localhost;127.0.0.1" /f`, { stdio: 'pipe' })
1363
- return true
1364
- } else if (process.platform === 'darwin') {
1365
- try {
1366
- const services = execSync('networksetup -listallnetworkservices', { encoding: 'utf-8' })
1367
- .split('\n')
1368
- .filter(s => s.trim() && !s.startsWith('*') && !s.startsWith('An asterisk'))
1369
- for (const svc of services) {
1370
- try {
1371
- execSync(`networksetup -setsecurewebproxy "${svc.trim()}" 127.0.0.1 ${port}`, { stdio: 'pipe' })
1372
- } catch {}
1373
- }
1374
- return true
1375
- } catch { return false }
1376
- } else {
1377
- try {
1378
- execSync(`gsettings set org.gnome.system.proxy mode 'manual'`, { stdio: 'pipe' })
1379
- execSync(`gsettings set org.gnome.system.proxy.https host '127.0.0.1'`, { stdio: 'pipe' })
1380
- execSync(`gsettings set org.gnome.system.proxy.https port ${port}`, { stdio: 'pipe' })
1381
- return true
1382
- } catch { return false }
1383
- }
1384
- } catch {
1385
- return false
1386
- }
1387
- }
1388
-
1389
- function cleanSystemProxy() {
1390
- try {
1391
- if (process.platform === 'win32') {
1392
- execSync(`reg add "HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings" /v ProxyEnable /t REG_DWORD /d 0 /f`, { stdio: 'pipe' })
1393
- } else if (process.platform === 'darwin') {
1394
- try {
1395
- const services = execSync('networksetup -listallnetworkservices', { encoding: 'utf-8' })
1396
- .split('\n')
1397
- .filter(s => s.trim() && !s.startsWith('*') && !s.startsWith('An asterisk'))
1398
- for (const svc of services) {
1399
- try {
1400
- execSync(`networksetup -setsecurewebproxystate "${svc.trim()}" off`, { stdio: 'pipe' })
1401
- } catch {}
1402
- }
1403
- } catch {}
1404
- } else {
1405
- try {
1406
- execSync(`gsettings set org.gnome.system.proxy mode 'none'`, { stdio: 'pipe' })
1407
- } catch {}
1408
- }
1409
- console.log('System proxy settings cleaned up.')
1410
- } catch {
1411
- console.warn('Could not clean system proxy settings. You may need to disable manually.')
1412
- }
1413
- }
1414
-
1415
1353
  // ── CLI router ────────────────────────────────────────────────────────────────
1416
1354
 
1417
1355
  switch (command) {
@@ -1529,14 +1467,6 @@ switch (command) {
1529
1467
  await startTunnel()
1530
1468
  break
1531
1469
 
1532
- case 'cursor':
1533
- if (args[1] === 'stop') {
1534
- cleanSystemProxy()
1535
- } else {
1536
- await startCursorProxy()
1537
- }
1538
- break
1539
-
1540
1470
  case 'uninstall':
1541
1471
  await uninstall()
1542
1472
  break
@@ -1544,6 +1474,12 @@ switch (command) {
1544
1474
  showConfig()
1545
1475
  break
1546
1476
 
1477
+ case 'mcp': {
1478
+ const subCmd = args[0] ?? 'install'
1479
+ if (subCmd === 'uninstall') await mcpUninstall()
1480
+ else await mcpInstall()
1481
+ break
1482
+ }
1547
1483
  case 'version':
1548
1484
  case '--version':
1549
1485
  case '-v':
package/dist/mcp.d.ts ADDED
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Squeezr MCP Server
4
+ *
5
+ * Gives any MCP-compatible AI CLI (Claude Code, Cursor, Windsurf, Cline…)
6
+ * real-time awareness of Squeezr's state and control over it.
7
+ *
8
+ * Transport: stdio (universal — works with all MCP clients)
9
+ * Queries the Squeezr proxy via HTTP on localhost.
10
+ *
11
+ * Tools exposed:
12
+ * squeezr_status — Is proxy running? Port, version, uptime
13
+ * squeezr_stats — Token savings, compression %, by-tool breakdown
14
+ * squeezr_set_mode — Change compression aggressiveness instantly
15
+ * squeezr_config — Current thresholds and settings
16
+ * squeezr_habits — Detected wasteful patterns this session
17
+ */
18
+ export {};