local-mcp 3.0.69 → 3.0.71
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 +1 -1
- package/download.js +46 -1
- package/index.js +5 -3
- package/package.json +1 -1
- package/setup.js +109 -19
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# LMCP
|
|
2
2
|
|
|
3
|
-
> Give your AI assistant native access to Mac apps —
|
|
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
|
[](https://www.npmjs.com/package/local-mcp)
|
|
6
6
|
[](https://local-mcp.com?ref=npm)
|
package/download.js
CHANGED
|
@@ -310,4 +310,49 @@ function _writeTrayLaunchAgent(trayApp) {
|
|
|
310
310
|
} catch { /* no crítico */ }
|
|
311
311
|
}
|
|
312
312
|
|
|
313
|
-
|
|
313
|
+
/**
|
|
314
|
+
* Descarga el co-proceso m365-proxy si no está instalado o es una versión diferente.
|
|
315
|
+
* El binary se instala en ~/.local/share/local-mcp/bin/m365-proxy
|
|
316
|
+
* @returns {Promise<string|null>} Ruta al binary, o null si falla
|
|
317
|
+
*/
|
|
318
|
+
async function ensureM365Proxy() {
|
|
319
|
+
const info = await getLatestBinary()
|
|
320
|
+
const version = info.version
|
|
321
|
+
const arch = process.arch === 'arm64' ? 'darwin-arm64' : 'darwin-x64'
|
|
322
|
+
const binPath = path.join(CACHE_DIR, 'm365-proxy')
|
|
323
|
+
const verFile = path.join(CACHE_DIR, '.m365-proxy-version')
|
|
324
|
+
|
|
325
|
+
// Ya instalado y actualizado
|
|
326
|
+
if (fs.existsSync(binPath) && fs.existsSync(verFile)) {
|
|
327
|
+
if (fs.readFileSync(verFile, 'utf8').trim() === version) return binPath
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
const url = `https://download.local-mcp.com/m365-proxy-${version}-${arch}.tar.gz`
|
|
331
|
+
process.stderr.write(`\nDescargando m365-proxy v${version}...\n`)
|
|
332
|
+
|
|
333
|
+
fs.mkdirSync(CACHE_DIR, { recursive: true })
|
|
334
|
+
const tarPath = path.join(CACHE_DIR, `m365-proxy-${version}.tar.gz`)
|
|
335
|
+
|
|
336
|
+
try {
|
|
337
|
+
await downloadFile(url, tarPath)
|
|
338
|
+
const stat = fs.statSync(tarPath)
|
|
339
|
+
if (stat.size < 1000) { fs.unlinkSync(tarPath); throw new Error(`Descarga incompleta (${stat.size} bytes)`) }
|
|
340
|
+
|
|
341
|
+
execFileSync('tar', ['-xzf', tarPath, '-C', CACHE_DIR], { stdio: 'pipe' })
|
|
342
|
+
fs.unlinkSync(tarPath)
|
|
343
|
+
|
|
344
|
+
if (!fs.existsSync(binPath)) throw new Error('m365-proxy no encontrado tras extraer')
|
|
345
|
+
fs.chmodSync(binPath, 0o755)
|
|
346
|
+
try { execFileSync('codesign', ['--force', '--sign', '-', '--identifier', 'com.local-mcp.m365-proxy', binPath], { stdio: 'pipe' }) } catch { /* no crítico */ }
|
|
347
|
+
|
|
348
|
+
fs.writeFileSync(verFile, version, 'utf8')
|
|
349
|
+
process.stderr.write(` m365-proxy instalado en ${binPath}\n`)
|
|
350
|
+
return binPath
|
|
351
|
+
} catch (err) {
|
|
352
|
+
try { if (fs.existsSync(tarPath)) fs.unlinkSync(tarPath) } catch {}
|
|
353
|
+
process.stderr.write(` (m365-proxy no disponible: ${err.message})\n`)
|
|
354
|
+
return null
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
module.exports = { ensureBinary, ensureRuntime, ensureTray, ensureTeamsProxy, ensureM365Proxy, CACHE_DIR, TRAY_DIR }
|
package/index.js
CHANGED
|
@@ -122,9 +122,10 @@ async function main() {
|
|
|
122
122
|
: ''
|
|
123
123
|
if (cachedVersion === pkg.version) {
|
|
124
124
|
// Version matches — use fast path
|
|
125
|
-
const { ensureTray, ensureTeamsProxy } = require('./download')
|
|
125
|
+
const { ensureTray, ensureTeamsProxy, ensureM365Proxy } = require('./download')
|
|
126
126
|
ensureTray().catch(() => {})
|
|
127
127
|
ensureTeamsProxy().catch(() => {})
|
|
128
|
+
ensureM365Proxy().catch(() => {})
|
|
128
129
|
const child = spawn(stableBin, [], { stdio: 'inherit', env: process.env })
|
|
129
130
|
child.on('error', () => process.exit(1))
|
|
130
131
|
child.on('exit', (code) => process.exit(code ?? 0))
|
|
@@ -143,11 +144,12 @@ async function main() {
|
|
|
143
144
|
}
|
|
144
145
|
|
|
145
146
|
// Slow path: download/repair binary
|
|
146
|
-
const { ensureRuntime, ensureTray, ensureTeamsProxy } = require('./download')
|
|
147
|
+
const { ensureRuntime, ensureTray, ensureTeamsProxy, ensureM365Proxy } = require('./download')
|
|
147
148
|
|
|
148
|
-
// Actualizar tray + teams-proxy en background
|
|
149
|
+
// Actualizar tray + teams-proxy + m365-proxy en background
|
|
149
150
|
ensureTray().catch(() => {})
|
|
150
151
|
ensureTeamsProxy().catch(() => {})
|
|
152
|
+
ensureM365Proxy().catch(() => {})
|
|
151
153
|
|
|
152
154
|
let runtime
|
|
153
155
|
try {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "local-mcp",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.71",
|
|
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
|
@@ -170,13 +170,32 @@ async function runSetup(opts = {}) {
|
|
|
170
170
|
const detected = CLIENTS.filter(c => c.detect())
|
|
171
171
|
|
|
172
172
|
if (detected.length === 0) {
|
|
173
|
-
|
|
174
|
-
console.log('Install Claude Desktop, Cursor, Windsurf, or VS Code and run this command again.\n')
|
|
175
|
-
console.log('Or add this manually to your AI client config:')
|
|
176
|
-
const manualEntry = stableArgs !== undefined
|
|
173
|
+
const entry = stableArgs !== undefined
|
|
177
174
|
? { command: stableCommand, args: stableArgs }
|
|
178
175
|
: { command: stableCommand }
|
|
179
|
-
|
|
176
|
+
const snippet = JSON.stringify({ mcpServers: { 'local-mcp': entry } }, null, 2)
|
|
177
|
+
.split('\n').map(l => ' ' + l).join('\n')
|
|
178
|
+
|
|
179
|
+
console.log('╔══════════════════════════════════════════════════════════╗')
|
|
180
|
+
console.log('║ LMCP installed — no AI client detected yet ║')
|
|
181
|
+
console.log('╚══════════════════════════════════════════════════════════╝\n')
|
|
182
|
+
console.log(' To get started, add LMCP to your AI client config:\n')
|
|
183
|
+
console.log(' ── Claude Desktop ────────────────────────────────────────')
|
|
184
|
+
console.log(' File: ~/Library/Application Support/Claude/claude_desktop_config.json\n')
|
|
185
|
+
console.log(snippet)
|
|
186
|
+
console.log('\n ── Cursor ────────────────────────────────────────────────')
|
|
187
|
+
console.log(' File: ~/.cursor/mcp.json\n')
|
|
188
|
+
console.log(snippet)
|
|
189
|
+
console.log('\n ── VS Code (Cline / GitHub Copilot) ──────────────────────')
|
|
190
|
+
console.log(' File: ~/.vscode/mcp.json\n')
|
|
191
|
+
console.log(snippet)
|
|
192
|
+
console.log('\n Then restart your AI client and try:')
|
|
193
|
+
console.log(' "Summarize my unread emails"\n')
|
|
194
|
+
console.log(' ── Already installed an AI client? ───────────────────────')
|
|
195
|
+
console.log(' Run this to auto-configure it:')
|
|
196
|
+
console.log(' npx local-mcp setup\n')
|
|
197
|
+
console.log(' Full setup guide: https://local-mcp.com/setup')
|
|
198
|
+
console.log('─────────────────────────────────────────────────────────────\n')
|
|
180
199
|
return
|
|
181
200
|
}
|
|
182
201
|
|
|
@@ -196,11 +215,12 @@ async function runSetup(opts = {}) {
|
|
|
196
215
|
}
|
|
197
216
|
}
|
|
198
217
|
|
|
218
|
+
const cfgDir = path.join(HOME, 'Library', 'Application Support', 'Local MCP')
|
|
219
|
+
const cfgFile = path.join(cfgDir, 'config.json')
|
|
220
|
+
|
|
199
221
|
// Escribir email al config si viene por env
|
|
200
|
-
|
|
222
|
+
let email = process.env.LMCP_EMAIL || ''
|
|
201
223
|
if (email) {
|
|
202
|
-
const cfgDir = path.join(HOME, 'Library', 'Application Support', 'Local MCP')
|
|
203
|
-
const cfgFile = path.join(cfgDir, 'config.json')
|
|
204
224
|
try {
|
|
205
225
|
const cfg = _readJson(cfgFile)
|
|
206
226
|
if (!cfg.license_email) {
|
|
@@ -214,34 +234,75 @@ async function runSetup(opts = {}) {
|
|
|
214
234
|
// Health check — verify binary works before showing success
|
|
215
235
|
const healthOk = _runHealthCheck()
|
|
216
236
|
|
|
237
|
+
const hasCursor = configured.includes('Cursor')
|
|
238
|
+
|
|
217
239
|
console.log('\n──────────────────────────────────────')
|
|
218
240
|
if (configured.length > 0) {
|
|
219
241
|
if (healthOk) {
|
|
220
242
|
console.log(`✅ LMCP configured for: ${configured.join(', ')}\n`)
|
|
221
|
-
console.log(' ✓ Server binary verified —
|
|
243
|
+
console.log(' ✓ Server binary verified — 95 tools ready\n')
|
|
222
244
|
} else {
|
|
223
245
|
console.log(`⚠ LMCP configured for: ${configured.join(', ')}`)
|
|
224
246
|
console.log(' Server binary could not be verified — it may still work after restart.\n')
|
|
225
247
|
}
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
248
|
+
|
|
249
|
+
if (hasCursor) {
|
|
250
|
+
console.log('┌─────────────────────────────────────────────────────┐')
|
|
251
|
+
console.log('│ CURSOR — 3 steps to activate: │')
|
|
252
|
+
console.log('│ │')
|
|
253
|
+
console.log('│ 1. Quit Cursor completely (Cmd+Q) │')
|
|
254
|
+
console.log('│ 2. Reopen Cursor │')
|
|
255
|
+
console.log('│ 3. Cursor Settings (⚙) → MCP → find "local-mcp" │')
|
|
256
|
+
console.log('│ → toggle it ON if not already enabled │')
|
|
257
|
+
console.log('│ │')
|
|
258
|
+
console.log('│ Then ask: "Summarize my unread emails" │')
|
|
259
|
+
console.log('│ │')
|
|
260
|
+
console.log('│ Guide: local-mcp.com/guides/cursor-mcp-setup │')
|
|
261
|
+
console.log('└─────────────────────────────────────────────────────┘\n')
|
|
262
|
+
} else {
|
|
263
|
+
const primaryClient = configured[0]
|
|
264
|
+
console.log('┌─────────────────────────────────────────────────────┐')
|
|
265
|
+
console.log('│ NEXT STEP — This is important: │')
|
|
266
|
+
console.log('│ │')
|
|
267
|
+
console.log(`│ Quit and reopen ${primaryClient.padEnd(35)}│`)
|
|
268
|
+
console.log('│ LMCP will appear automatically. │')
|
|
269
|
+
console.log('│ │')
|
|
270
|
+
console.log('│ Then try: "Summarize my unread emails" │')
|
|
271
|
+
console.log('└─────────────────────────────────────────────────────┘\n')
|
|
272
|
+
}
|
|
234
273
|
}
|
|
235
274
|
if (failed.length > 0) {
|
|
236
275
|
console.log(`⚠ Could not configure: ${failed.join(', ')} — check permissions and try again.\n`)
|
|
237
276
|
}
|
|
277
|
+
console.log(' Setup guide & troubleshooting: https://local-mcp.com/setup\n')
|
|
238
278
|
|
|
239
279
|
// Instalar y lanzar el tray (menu bar app) — arm64 only, non-fatal
|
|
240
280
|
await _installTray()
|
|
241
281
|
await _installTeamsProxy()
|
|
242
282
|
|
|
283
|
+
// Interactive email prompt — only when running in a TTY and no email set yet
|
|
284
|
+
if (!email) {
|
|
285
|
+
const existingEmail = (() => {
|
|
286
|
+
try { return _readJson(cfgFile).license_email || '' } catch { return '' }
|
|
287
|
+
})()
|
|
288
|
+
if (!existingEmail && process.stdin.isTTY && process.stdout.isTTY) {
|
|
289
|
+
const prompted = await _promptEmail()
|
|
290
|
+
if (prompted) {
|
|
291
|
+
try {
|
|
292
|
+
const cfg = _readJson(cfgFile)
|
|
293
|
+
if (!cfg.license_email) {
|
|
294
|
+
cfg.license_email = prompted
|
|
295
|
+
_writeJson(cfgFile, cfg)
|
|
296
|
+
console.log(` ✓ Email saved — we'll notify you about important updates`)
|
|
297
|
+
}
|
|
298
|
+
} catch { /* config will be created on first server run */ }
|
|
299
|
+
email = prompted
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
243
304
|
// Ping de tracking (fire-and-forget, no bloquea)
|
|
244
|
-
_pingInstall(configured, opts.method || 'setup')
|
|
305
|
+
_pingInstall(configured, opts.method || 'setup', email)
|
|
245
306
|
}
|
|
246
307
|
|
|
247
308
|
async function _installTeamsProxy() {
|
|
@@ -313,7 +374,34 @@ function _getMachineId() {
|
|
|
313
374
|
return ''
|
|
314
375
|
}
|
|
315
376
|
|
|
316
|
-
|
|
377
|
+
/// Ask the user for their email address during the terminal setup wizard.
|
|
378
|
+
/// Returns the trimmed email string, or '' if the user skipped or timed out.
|
|
379
|
+
/// Only called when stdin+stdout are both TTYs.
|
|
380
|
+
async function _promptEmail() {
|
|
381
|
+
return new Promise((resolve) => {
|
|
382
|
+
const readline = require('readline')
|
|
383
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout, terminal: false })
|
|
384
|
+
let answered = false
|
|
385
|
+
const timer = setTimeout(() => {
|
|
386
|
+
if (!answered) { answered = true; try { rl.close() } catch {} resolve('') }
|
|
387
|
+
}, 30000)
|
|
388
|
+
process.stdout.write('\n──────────────────────────────────────\n')
|
|
389
|
+
process.stdout.write(' 📧 Your email (optional — for update notifications):\n → ')
|
|
390
|
+
rl.once('line', (line) => {
|
|
391
|
+
if (answered) return
|
|
392
|
+
answered = true
|
|
393
|
+
clearTimeout(timer)
|
|
394
|
+
rl.close()
|
|
395
|
+
const trimmed = (line || '').trim()
|
|
396
|
+
resolve(trimmed.includes('@') && trimmed.includes('.') ? trimmed : '')
|
|
397
|
+
})
|
|
398
|
+
rl.once('close', () => {
|
|
399
|
+
if (!answered) { answered = true; clearTimeout(timer); resolve('') }
|
|
400
|
+
})
|
|
401
|
+
})
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
function _pingInstall(clients, method, email = '') {
|
|
317
405
|
try {
|
|
318
406
|
const https = require('https')
|
|
319
407
|
const pkg = require('./package.json')
|
|
@@ -326,6 +414,7 @@ function _pingInstall(clients, method) {
|
|
|
326
414
|
method: method,
|
|
327
415
|
clients_configured: clients,
|
|
328
416
|
machine_id: machineId,
|
|
417
|
+
email: email || '',
|
|
329
418
|
source: process.env.LMCP_SOURCE || '',
|
|
330
419
|
ref: _getRef(),
|
|
331
420
|
})
|
|
@@ -369,6 +458,7 @@ function _pingInstall(clients, method) {
|
|
|
369
458
|
arch: process.arch,
|
|
370
459
|
transport: 'stdio',
|
|
371
460
|
ai_client: clients[0] || '',
|
|
461
|
+
email: email || '',
|
|
372
462
|
started_at: new Date().toISOString(),
|
|
373
463
|
})
|
|
374
464
|
const hbReq = https.request({
|