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 +19 -1
- package/bin/squeezr.js +102 -166
- package/dist/mcp.d.ts +18 -0
- package/dist/mcp.js +380 -0
- package/package.json +7 -3
- package/dist/__tests__/cursorMitm.test.d.ts +0 -1
- package/dist/__tests__/cursorMitm.test.js +0 -313
- package/dist/cursorMitm.d.ts +0 -18
- package/dist/cursorMitm.js +0 -846
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 {};
|