local-mcp 3.0.68 → 3.0.70

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.
Files changed (3) hide show
  1. package/README.md +1 -1
  2. package/package.json +1 -1
  3. package/setup.js +56 -5
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # LMCP
2
2
 
3
- > Give your AI assistant native access to Mac apps — 92 tools for Mail, Calendar, Teams, OneDrive, and more. Everything runs locally. Your data never leaves your machine.
3
+ > Give your AI assistant native access to Mac apps — 95 tools for Mail, Calendar, Teams, OneDrive, and more. Everything runs locally. Your data never leaves your machine.
4
4
 
5
5
  [![npm](https://img.shields.io/npm/v/local-mcp)](https://www.npmjs.com/package/local-mcp)
6
6
  [![macOS 12+](https://img.shields.io/badge/macOS-12%2B-blue)](https://local-mcp.com?ref=npm)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "local-mcp",
3
- "version": "3.0.68",
3
+ "version": "3.0.70",
4
4
  "description": "LMCP — connect Claude Desktop, Cursor, Windsurf to Mail, Calendar, Contacts, Teams, OneDrive on macOS. Privacy-first: all data stays on your Mac.",
5
5
  "main": "index.js",
6
6
  "bin": {
package/setup.js CHANGED
@@ -196,11 +196,12 @@ async function runSetup(opts = {}) {
196
196
  }
197
197
  }
198
198
 
199
+ const cfgDir = path.join(HOME, 'Library', 'Application Support', 'Local MCP')
200
+ const cfgFile = path.join(cfgDir, 'config.json')
201
+
199
202
  // Escribir email al config si viene por env
200
- const email = process.env.LMCP_EMAIL || ''
203
+ let email = process.env.LMCP_EMAIL || ''
201
204
  if (email) {
202
- const cfgDir = path.join(HOME, 'Library', 'Application Support', 'Local MCP')
203
- const cfgFile = path.join(cfgDir, 'config.json')
204
205
  try {
205
206
  const cfg = _readJson(cfgFile)
206
207
  if (!cfg.license_email) {
@@ -240,8 +241,29 @@ async function runSetup(opts = {}) {
240
241
  await _installTray()
241
242
  await _installTeamsProxy()
242
243
 
244
+ // Interactive email prompt — only when running in a TTY and no email set yet
245
+ if (!email) {
246
+ const existingEmail = (() => {
247
+ try { return _readJson(cfgFile).license_email || '' } catch { return '' }
248
+ })()
249
+ if (!existingEmail && process.stdin.isTTY && process.stdout.isTTY) {
250
+ const prompted = await _promptEmail()
251
+ if (prompted) {
252
+ try {
253
+ const cfg = _readJson(cfgFile)
254
+ if (!cfg.license_email) {
255
+ cfg.license_email = prompted
256
+ _writeJson(cfgFile, cfg)
257
+ console.log(` ✓ Email saved — we'll notify you about important updates`)
258
+ }
259
+ } catch { /* config will be created on first server run */ }
260
+ email = prompted
261
+ }
262
+ }
263
+ }
264
+
243
265
  // Ping de tracking (fire-and-forget, no bloquea)
244
- _pingInstall(configured, opts.method || 'setup')
266
+ _pingInstall(configured, opts.method || 'setup', email)
245
267
  }
246
268
 
247
269
  async function _installTeamsProxy() {
@@ -313,7 +335,34 @@ function _getMachineId() {
313
335
  return ''
314
336
  }
315
337
 
316
- function _pingInstall(clients, method) {
338
+ /// Ask the user for their email address during the terminal setup wizard.
339
+ /// Returns the trimmed email string, or '' if the user skipped or timed out.
340
+ /// Only called when stdin+stdout are both TTYs.
341
+ async function _promptEmail() {
342
+ return new Promise((resolve) => {
343
+ const readline = require('readline')
344
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout, terminal: false })
345
+ let answered = false
346
+ const timer = setTimeout(() => {
347
+ if (!answered) { answered = true; try { rl.close() } catch {} resolve('') }
348
+ }, 30000)
349
+ process.stdout.write('\n──────────────────────────────────────\n')
350
+ process.stdout.write(' 📧 Your email (optional — for update notifications):\n → ')
351
+ rl.once('line', (line) => {
352
+ if (answered) return
353
+ answered = true
354
+ clearTimeout(timer)
355
+ rl.close()
356
+ const trimmed = (line || '').trim()
357
+ resolve(trimmed.includes('@') && trimmed.includes('.') ? trimmed : '')
358
+ })
359
+ rl.once('close', () => {
360
+ if (!answered) { answered = true; clearTimeout(timer); resolve('') }
361
+ })
362
+ })
363
+ }
364
+
365
+ function _pingInstall(clients, method, email = '') {
317
366
  try {
318
367
  const https = require('https')
319
368
  const pkg = require('./package.json')
@@ -326,6 +375,7 @@ function _pingInstall(clients, method) {
326
375
  method: method,
327
376
  clients_configured: clients,
328
377
  machine_id: machineId,
378
+ email: email || '',
329
379
  source: process.env.LMCP_SOURCE || '',
330
380
  ref: _getRef(),
331
381
  })
@@ -369,6 +419,7 @@ function _pingInstall(clients, method) {
369
419
  arch: process.arch,
370
420
  transport: 'stdio',
371
421
  ai_client: clients[0] || '',
422
+ email: email || '',
372
423
  started_at: new Date().toISOString(),
373
424
  })
374
425
  const hbReq = https.request({