local-mcp 3.0.414 → 3.0.417

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
@@ -1,12 +1,12 @@
1
1
  # Local MCP
2
2
 
3
- > **Use your Mac from ChatGPT & Claude.ai on the web** — plus 191+ local tools for Claude Desktop, Cursor, Windsurf, VS Code & Zed. Connect any AI to Mail, Calendar, Contacts, iMessage, Teams, Slack, WhatsApp, Signal, OneDrive, Google Drive, Microsoft 365, Notes, Reminders, OmniFocus, Word/Excel/PowerPoint and more. Runs on your computer, no API keys, free — local tools read your data on your machine, and your AI provider receives the content needed for each request.
3
+ > **Use your Mac from ChatGPT & Claude.ai on the web** — plus 192+ local tools for Claude Desktop, Cursor, Windsurf, VS Code & Zed. Connect any AI to Mail, Calendar, Contacts, iMessage, Teams, Slack, WhatsApp, Signal, OneDrive, Google Drive, Microsoft 365, Notes, Reminders, OmniFocus, Word/Excel/PowerPoint and more. Runs on your computer, no API keys, free — local tools read your data on your machine, and your AI provider receives the content needed for each request.
4
4
 
5
5
  [![npm](https://img.shields.io/npm/v/local-mcp?style=flat-square&label=npm)](https://www.npmjs.com/package/local-mcp)
6
6
  [![macOS](https://img.shields.io/badge/macOS-13%2B-111111?style=flat-square)](https://local-mcp.com?ref=npm)
7
7
  [![Signed](https://img.shields.io/badge/Developer%20ID-notarized-111111?style=flat-square)](https://local-mcp.com?ref=npm)
8
8
  [![Privacy](https://img.shields.io/badge/tools-run%20locally-2e7d32?style=flat-square)](https://local-mcp.com/en/privacy)
9
- [![Glama](https://glama.ai/mcp/servers/lanchuske/local-mcp-releases/badge)](https://glama.ai/mcp/servers/lanchuske/local-mcp-releases?ref=npm)
9
+ [![Glama](https://glama.ai/mcp/servers/colibird-ai/local-mcp-releases/badge)](https://glama.ai/mcp/servers/colibird-ai/local-mcp-releases?ref=npm)
10
10
 
11
11
  **Runs on your computer. No tokens. No API keys. Free.**
12
12
 
@@ -203,4 +203,4 @@ Stops all background processes, removes the LaunchAgent, deletes the app and bin
203
203
 
204
204
  ## Links
205
205
 
206
- [Website](https://local-mcp.com?ref=npm) · [GitHub](https://github.com/lanchuske/local-mcp-releases) · [Guides](https://local-mcp.com/guides) · Support: ctpo@colibird.co
206
+ [Website](https://local-mcp.com?ref=npm) · [GitHub](https://github.com/colibird-ai/local-mcp-releases) · [Guides](https://local-mcp.com/guides) · Support: ctpo@colibird.co
package/download.js CHANGED
@@ -31,6 +31,7 @@ function extractTar(tarPath, destDir) {
31
31
  }
32
32
 
33
33
  const BACKEND_URL = 'https://office-mcp-production.up.railway.app'
34
+ const LINUX_NO_SOPORTADO = 'Local MCP does not support Linux. It runs on macOS and Windows — https://local-mcp.com'
34
35
 
35
36
  // Platform-aware cache directory:
36
37
  // macOS: ~/.local/share/local-mcp/bin
@@ -178,7 +179,7 @@ function _getMachineId(deps = {}) {
178
179
  } else if (platform === 'win32') {
179
180
  const hw = _probeWinHardwareId(execSync)
180
181
  if (hw) {
181
- _writeWinMachineIdCache(cacheDir, hw)
182
+ if (!deps.soloLectura) _writeWinMachineIdCache(cacheDir, hw)
182
183
  return hw
183
184
  }
184
185
  // Both probes failed or are blocked — fall back to whatever the go-server
@@ -193,13 +194,17 @@ function _getMachineId(deps = {}) {
193
194
  if (mid) return mid
194
195
  } catch {}
195
196
  }
197
+ // El evento de lanzamiento (launch-event.js) pide soloLectura: no escribe el cache de arriba —
198
+ // en una máquina desinstalada recrearía el directorio que el usuario borró — y prefiere no
199
+ // tener id antes que uno derivado del hostname.
200
+ if (deps.soloLectura) return ''
196
201
  // Fallback: hash hostname instead of sending raw PII
197
202
  const crypto = require('crypto')
198
203
  return crypto.createHash('sha256').update(os.hostname()).digest('hex').slice(0, 16)
199
204
  }
200
205
 
201
- function getArch() {
202
- const plat = process.platform === 'win32' ? 'windows' : process.platform === 'linux' ? 'linux' : 'darwin'
206
+ function getArch(platform = process.platform) {
207
+ const plat = platform === 'win32' ? 'windows' : platform === 'linux' ? 'linux' : 'darwin'
203
208
  const arch = process.arch
204
209
  if (arch === 'arm64') return `${plat}-arm64`
205
210
  if (arch === 'x64') return `${plat}-amd64`
@@ -330,15 +335,26 @@ async function downloadFile(url, destPath) {
330
335
  * Asegura que el binario esté descargado y listo.
331
336
  * @returns {Promise<{binPath: string}>}
332
337
  */
333
- async function ensureBinary() {
334
- const arch = getArch()
335
- const info = await getLatestBinary()
338
+ async function ensureBinary(deps = {}) {
339
+ // Linux no está soportado (decisión de Dario, 2026-10-02; colibird-ai/lmcp-npm#9). Se dice
340
+ // ANTES de pedir nada: ni /runtime/latest ni un tarball. Anuncie lo que anuncie el API — llegó a
341
+ // anunciar URLs `linux_*` que daban 404, y la guarda vieja (sólo «si no hay URL») no saltaba.
342
+ if ((deps.platform || process.platform) === 'linux') throw new Error(LINUX_NO_SOPORTADO)
343
+ // Seam for tests (same idiom as ensureTray's `deps`, #2654): lets a test pin the
344
+ // platform and stub the network without touching process.platform or real HOME.
345
+ const {
346
+ platform = process.platform,
347
+ getLatest = getLatestBinary,
348
+ download = downloadFile,
349
+ } = deps
350
+ const arch = getArch(platform)
351
+ const info = await getLatest()
336
352
  const version = info.version
337
353
 
338
354
  // On non-darwin, use the Go server binary (cross-platform).
339
355
  // The Go binary is a single file, not a nested tarball.
340
- if (process.platform !== 'darwin') {
341
- const binName = process.platform === 'win32' ? 'lmcp-server.exe' : 'lmcp-server'
356
+ if (platform !== 'darwin') {
357
+ const binName = platform === 'win32' ? 'lmcp-server.exe' : 'lmcp-server'
342
358
  const binPath = path.join(CACHE_DIR, binName)
343
359
  const versionFile = path.join(CACHE_DIR, '.go-server-version')
344
360
 
@@ -390,7 +406,7 @@ async function ensureBinary() {
390
406
  const tarPath = path.join(CACHE_DIR, `go-server-${platformVersion}.tar.gz`)
391
407
 
392
408
  try {
393
- await downloadFile(url, tarPath)
409
+ await download(url, tarPath)
394
410
  process.stderr.write(` Extracting...\n`)
395
411
  extractTar(tarPath, CACHE_DIR)
396
412
  fs.unlinkSync(tarPath)
@@ -415,7 +431,7 @@ async function ensureBinary() {
415
431
  if (!fs.existsSync(binPath)) {
416
432
  throw new Error(`Binary not found after extraction: ${binPath}`)
417
433
  }
418
- if (process.platform !== 'win32') fs.chmodSync(binPath, 0o755)
434
+ if (platform !== 'win32') fs.chmodSync(binPath, 0o755)
419
435
  fs.writeFileSync(versionFile, platformVersion)
420
436
 
421
437
  process.stderr.write(` Ready at ${CACHE_DIR}\n`)
@@ -853,7 +869,7 @@ async function ensureSlackProxy(platform = process.platform) {
853
869
  module.exports = {
854
870
  ensureBinary, ensureRuntime, ensureTray, ensureTeamsProxy, ensureSlackProxy, versionFromArtifactUrl,
855
871
  latestVersionForPlatform, getArch,
856
- CACHE_DIR, TRAY_DIR, _getMachineId,
872
+ CACHE_DIR, TRAY_DIR, _getMachineId, LINUX_NO_SOPORTADO,
857
873
  _probeWinHardwareId, _resetWinHardwareIdCacheForTests,
858
874
  _readWinMachineIdCache, _writeWinMachineIdCache, WIN_MACHINE_ID_CACHE_NAME,
859
875
  _retireSupersededTrays, _isUsableTrayBundle, SYSTEM_TRAY_APP,
package/index.js CHANGED
@@ -17,6 +17,7 @@ const path = require('path')
17
17
  const os = require('os')
18
18
  const fs = require('fs')
19
19
  const https = require('https')
20
+ const evento = require('./launch-event')
20
21
 
21
22
  // ── Version + liveness helpers (module scope so they are testable) ────────────
22
23
 
@@ -232,12 +233,30 @@ function clearUninstallSentinels() {
232
233
  for (const p of uninstallSentinelPaths()) { try { fs.unlinkSync(p) } catch {} }
233
234
  }
234
235
 
235
- // Platform support: macOS (Swift tray + embedded server), Windows & Linux
236
- // (go-server standalone binary downloaded from R2). Re-enabled for the
237
- // Windows/Linux public re-launch — the waitlist gate is retired.
236
+ const cmd = process.argv[2]
237
+
238
+ // Comandos que este wrapper entiende. Sin argumentos arranca el server MCP por stdio, que es
239
+ // como lo lanzan los clientes de IA — ese camino NO cambia.
240
+ const COMANDOS = ['setup', 'update', 'uninstall', 'status']
241
+ const PEDIDOS_DE_AYUDA = ['--help', '-h', 'help']
242
+
243
+ // Evento de lanzamiento (launch-event.js, #11): cada lanzamiento del camino del server reporta
244
+ // UNA vez cómo terminó. Un lanzamiento es: sin argumento, `stdio`, o un argumento desconocido.
245
+ // Los comandos y la ayuda no lo son y no reportan. El `exit` de abajo es la red: una salida de
246
+ // ese camino que nadie nombró —una excepción incluida— sale como `other`, nunca en silencio.
247
+ const ES_LANZAMIENTO = require.main === module && !COMANDOS.includes(cmd) && !PEDIDOS_DE_AYUDA.includes(cmd)
248
+ function reportarLanzamiento(outcome, opciones) {
249
+ if (ES_LANZAMIENTO) evento.reportar(outcome, evento.claseDeArgumento(cmd), opciones)
250
+ }
251
+ if (ES_LANZAMIENTO) process.on('exit', () => reportarLanzamiento('other'))
252
+
253
+ // Platform support: macOS (Swift tray + embedded server) and Windows (go-server standalone
254
+ // binary downloaded from R2). Linux passes this guard so that `--help`, `status`, `update` and
255
+ // `uninstall` answer, but it is NOT supported: the server path and `setup` say so (#9).
238
256
  const SUPPORTED_PLATFORMS = ['darwin', 'win32', 'linux']
239
257
  if (!SUPPORTED_PLATFORMS.includes(process.platform)) {
240
- console.error(`LMCP supports macOS, Windows and Linux. Your platform (${process.platform}) is not supported yet — see https://local-mcp.com`)
258
+ console.error(`LMCP supports macOS and Windows. Your platform (${process.platform}) is not supported yet — see https://local-mcp.com`)
259
+ reportarLanzamiento('unsupported-platform')
241
260
  process.exit(1)
242
261
  }
243
262
 
@@ -250,13 +269,12 @@ if (parseInt(process.version.slice(1)) < 16) {
250
269
  `If you use nvm, run: nvm alias default 22 && nvm uninstall ${process.version}\n` +
251
270
  'Then restart Claude Desktop / Cursor.'
252
271
  process.stderr.write(msg + '\n')
272
+ reportarLanzamiento('unsupported-node')
253
273
  // Exit with a non-zero code so the MCP host marks the server as failed
254
274
  // (vs hanging forever with no output).
255
275
  process.exit(1)
256
276
  }
257
277
 
258
- const cmd = process.argv[2]
259
-
260
278
  // Detect a MANUAL uninstall — the user dragged LocalMCPTray.app to the Trash (by far the
261
279
  // most common way to remove a Mac app) instead of running `npx local-mcp uninstall` or the
262
280
  // tray's Uninstall button, so no `.uninstalled` sentinel was written. Signature: the tray
@@ -279,15 +297,18 @@ function detectManualUninstall() {
279
297
  )
280
298
  }
281
299
 
282
- // Comandos que este wrapper entiende. Sin argumentos arranca el server MCP por stdio, que es
283
- // como lo lanzan los clientes de IA — ese camino NO cambia.
284
- const COMANDOS = ['setup', 'update', 'uninstall', 'status']
300
+ // `stdio` no es un comando: es el modo por defecto dicho en voz alta. Las guías públicas
301
+ // mandaron a escribir `"args": ["-y", "local-mcp@latest", "stdio"]` en la config manual de un
302
+ // cliente de IA, y esas configs siguen en máquinas de usuarios. Tratarlo como desconocido
303
+ // (3.0.413 a 3.0.416) las rompió: ayuda por stdout y exit 2 donde el cliente espera JSON-RPC.
304
+ const SINONIMOS_DEL_SERVER = ['stdio']
285
305
 
286
306
  function mostrarAyuda() {
287
307
  console.log(`local-mcp — Local MCP Server
288
308
 
289
309
  Uso:
290
310
  local-mcp arranca el server MCP por stdio (lo que hace un cliente de IA)
311
+ local-mcp stdio lo mismo, dicho explícitamente
291
312
  local-mcp setup instala y configura
292
313
  local-mcp status muestra el estado
293
314
  local-mcp update limpia el cache del runtime
@@ -303,11 +324,12 @@ async function main() {
303
324
  // sólo buscaba ayuda de CLI. Un comando de consulta no instala software.
304
325
  //
305
326
  // Sin argumentos sigue arrancando el server, que es como lo invocan los clientes de IA.
306
- if (cmd !== undefined && !COMANDOS.includes(cmd)) {
307
- const pedidoDeAyuda = ['--help', '-h', 'help'].includes(cmd)
327
+ if (cmd !== undefined && !COMANDOS.includes(cmd) && !SINONIMOS_DEL_SERVER.includes(cmd)) {
328
+ const pedidoDeAyuda = PEDIDOS_DE_AYUDA.includes(cmd)
308
329
  mostrarAyuda()
309
330
  if (!pedidoDeAyuda) {
310
331
  console.error(`\nNo conozco el comando "${cmd}".`)
332
+ reportarLanzamiento('rejected-argument')
311
333
  }
312
334
  process.exit(pedidoDeAyuda ? 0 : 2)
313
335
  }
@@ -317,7 +339,8 @@ async function main() {
317
339
  clearUninstallSentinels()
318
340
  const { runSetup } = require('./setup')
319
341
  await runSetup()
320
- process.exit(0)
342
+ // En Linux el setup no instaló nada (#9): quien lo encadena en un script tiene que verlo.
343
+ process.exit(process.platform === 'linux' ? 1 : 0)
321
344
  }
322
345
 
323
346
  if (cmd === 'update') {
@@ -402,6 +425,7 @@ async function main() {
402
425
  // runtime-dir and durable App Support copies (see uninstallSentinelPaths).
403
426
  {
404
427
  if (anyUninstallSentinel()) {
428
+ reportarLanzamiento('uninstalled')
405
429
  process.stderr.write(
406
430
  '[LMCP] Local MCP was uninstalled — not starting.\n' +
407
431
  ' Reinstall: npx local-mcp setup\n' +
@@ -421,6 +445,7 @@ async function main() {
421
445
  // and we short-circuit here, which is harmless.
422
446
  {
423
447
  if (detectManualUninstall()) {
448
+ reportarLanzamiento('uninstalled')
424
449
  writeUninstallSentinels()
425
450
  const trayPlist = path.join(os.homedir(), 'Library', 'LaunchAgents', 'com.local-mcp.tray.plist')
426
451
  if (fs.existsSync(trayPlist)) {
@@ -435,6 +460,15 @@ async function main() {
435
460
  }
436
461
  }
437
462
 
463
+ // ── Linux no está soportado (#9) ────────────────────────────────────────────
464
+ // Se dice acá, antes de cualquier descarga: sin esto el wrapper pedía un binario que no se
465
+ // publica y terminaba en un 404. Va después de los chequeos de desinstalación, que no bajan nada.
466
+ if (process.platform === 'linux') {
467
+ process.stderr.write(require('./download').LINUX_NO_SOPORTADO + '\n')
468
+ reportarLanzamiento('unsupported-platform')
469
+ process.exit(1)
470
+ }
471
+
438
472
  // ── Modo default: stdio MCP server ──────────────────────────────────────────
439
473
  const { CACHE_DIR } = require('./download')
440
474
  // macOS ships the Swift `local-mcp-server` (standalone fallback to the embedded
@@ -526,7 +560,10 @@ async function main() {
526
560
  try { _pkgVer = require('./package.json').version || '' } catch {}
527
561
  const child = spawn(binaryPath, spawnArgs, { stdio: 'inherit', env: { ...process.env, LMCP_NPM_VERSION: _pkgVer } })
528
562
  noteSpawnedServer(child.pid)
563
+ // El server ya arrancó y este proceso queda vivo toda la sesión: el evento sale desde acá.
564
+ child.on('spawn', () => reportarLanzamiento('spawned', { enProceso: true }))
529
565
  child.on('error', (err) => {
566
+ reportarLanzamiento('spawn-failed')
530
567
  process.stderr.write(`Error ejecutando LMCP: ${err.message}\n`)
531
568
  process.exit(1)
532
569
  })
@@ -667,6 +704,7 @@ async function main() {
667
704
  try {
668
705
  runtime = await ensureRuntime()
669
706
  } catch (err) {
707
+ reportarLanzamiento('download-failed')
670
708
  process.stderr.write(`\nError al preparar el runtime de LMCP:\n${err.message}\n`)
671
709
  process.stderr.write(`Intentá reinstalar: npx local-mcp update\n\n`)
672
710
  process.exit(1)
@@ -0,0 +1,115 @@
1
+ #!/usr/bin/env node
2
+ 'use strict'
3
+ // launch-event.js — el evento de lanzamiento del wrapper (colibird-ai/lmcp-npm#11, parte C).
4
+ //
5
+ // Por qué existe: de 3.0.413 a 3.0.416 el wrapper rechazó `stdio` y ninguna config manual
6
+ // conectaba. Nadie lo vio en diez días: el wrapper salía antes de que existiera telemetría.
7
+ //
8
+ // Contrato: contracts/launch-telemetry.v1.json de lmcp-api (copia fijada en npm/contracts/, que
9
+ // NO se publica: por eso los conjuntos están acá como constantes, y un test los compara).
10
+ //
11
+ // Corre en cada arranque de cada cliente de IA. Lo que no puede pasar nunca:
12
+ // · escribir en stdout — es el protocolo;
13
+ // · demorar el arranque del server o una salida — sin reintentos, timeout corto, todo error
14
+ // tragado;
15
+ // · mandar algo que no sea uno de los cinco campos: ni el argumento, ni rutas, ni entorno.
16
+ const https = require('https')
17
+ const { spawn } = require('child_process')
18
+ const DESTINO = { hostname: 'office-mcp-production.up.railway.app', path: '/launch-event' }
19
+ const PLATAFORMAS = ['darwin', 'win32', 'linux', 'other']
20
+ const CLASES = ['none', 'stdio', 'unknown']
21
+ const OUTCOMES = ['spawned', 'rejected-argument', 'unsupported-node', 'unsupported-platform',
22
+ 'download-failed', 'spawn-failed', 'uninstalled', 'other']
23
+ const RE_VERSION = /^\d{1,3}\.\d{1,3}\.\d{1,5}$/
24
+ const RE_MAQUINA = /^[A-Za-z0-9._:{}-]{1,64}$/
25
+ const [TIMEOUT_MS, TOPE_MS] = [3000, 4000] // el pedido · la vida del proceso suelto
26
+
27
+ // Del argumento sólo viaja su clase. `stdio` es el único que se nombra: es el del incidente.
28
+ function claseDeArgumento (cmd) {
29
+ if (cmd === undefined) return 'none'
30
+ return cmd === 'stdio' ? 'stdio' : 'unknown'
31
+ }
32
+
33
+ // El heartbeat no tiene compuerta de consentimiento (ver #11). Este evento respeta además el
34
+ // único interruptor que ya existe en el server de Mac.
35
+ function apagado (env = process.env) {
36
+ return ['1', 'true', 'yes'].includes(String(env.LMCP_DISABLE_TELEMETRY || '').toLowerCase())
37
+ }
38
+
39
+ // El id que usa el heartbeat, sin escribir nada y sin el fallback del hostname. Lo que no
40
+ // calza con el patrón del contrato (una ruta en LMCP_MACHINE_ID, por ejemplo) no viaja.
41
+ function idDeMaquina () {
42
+ try {
43
+ const id = require('./download')._getMachineId({ soloLectura: true })
44
+ return RE_MAQUINA.test(id) ? id : undefined
45
+ } catch { return undefined }
46
+ }
47
+
48
+ // null = no se manda. Sólo salen valores de los conjuntos del contrato.
49
+ function cuerpo (outcome, clase, deps = {}) {
50
+ const version = 'version' in deps ? deps.version : require('./package.json').version
51
+ const plataforma = deps.platform || process.platform
52
+ if (!OUTCOMES.includes(outcome) || !CLASES.includes(clase) || !RE_VERSION.test(version)) return null
53
+ const c = {
54
+ package_version: version,
55
+ platform: PLATAFORMAS.includes(plataforma) ? plataforma : 'other',
56
+ arg_class: clase,
57
+ outcome
58
+ }
59
+ const id = 'machineId' in deps ? deps.machineId : idDeMaquina()
60
+ if (typeof id === 'string' && RE_MAQUINA.test(id)) c.machine_id = id
61
+ return c
62
+ }
63
+
64
+ // Un POST, sin reintento, respuesta ignorada. `soltar` es para el pedido que sale desde el
65
+ // wrapper vivo: el socket no retiene al proceso.
66
+ function enviar (c, { soltar = false } = {}) {
67
+ try {
68
+ const datos = JSON.stringify(c)
69
+ const req = https.request({
70
+ ...DESTINO,
71
+ method: 'POST',
72
+ headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(datos) },
73
+ timeout: TIMEOUT_MS
74
+ }, (res) => res.resume())
75
+ req.on('error', () => {})
76
+ req.on('timeout', () => { try { req.destroy() } catch {} })
77
+ if (soltar) req.on('socket', (s) => { try { s.unref() } catch {} })
78
+ req.end(datos)
79
+ } catch {}
80
+ }
81
+
82
+ // Un evento por lanzamiento: la primera llamada decide, las demás no hacen nada.
83
+ // enProceso el server ya arrancó y el wrapper queda vivo toda la sesión (`spawned`): el
84
+ // pedido sale desde acá, sin proceso extra.
85
+ // si no el wrapper está por salir: un proceso suelto manda el evento y el wrapper no
86
+ // lo espera. Sin stdout heredado y, en Windows, sin ventana de consola.
87
+ // Una persona en una terminal (stdin TTY) no es un cliente de IA lanzando el server: no reporta.
88
+ let reportado = false
89
+ function reportar (outcome, clase, { enProceso = false } = {}) {
90
+ if (reportado) return
91
+ reportado = true
92
+ try {
93
+ if (apagado() || (process.stdin && process.stdin.isTTY)) return
94
+ if (!OUTCOMES.includes(outcome) || !CLASES.includes(clase)) return
95
+ if (enProceso) {
96
+ // Windows: el id sale de un PowerShell, que acá no se lanza. Sin id, ese `spawned` no se une a un heartbeat.
97
+ const c = cuerpo(outcome, clase, process.platform === 'win32' ? { machineId: process.env.LMCP_MACHINE_ID } : {})
98
+ if (c) enviar(c, { soltar: true })
99
+ return
100
+ }
101
+ const hijo = spawn(process.execPath, [__filename, outcome, clase],
102
+ { detached: true, stdio: 'ignore', windowsHide: true })
103
+ hijo.on('error', () => {})
104
+ hijo.unref()
105
+ } catch {}
106
+ }
107
+
108
+ // Como programa es el proceso suelto: `node launch-event.js <outcome> <clase>`.
109
+ if (require.main === module) {
110
+ setTimeout(() => process.exit(0), TOPE_MS).unref()
111
+ const c = apagado() ? null : cuerpo(process.argv[2], process.argv[3])
112
+ if (c) enviar(c)
113
+ }
114
+
115
+ module.exports = { PLATAFORMAS, CLASES, OUTCOMES, RE_VERSION, RE_MAQUINA, claseDeArgumento, apagado, cuerpo, reportar }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "local-mcp",
3
- "version": "3.0.414",
3
+ "version": "3.0.417",
4
4
  "description": "Let ChatGPT, Claude, Cursor & any MCP client actually use your Mac or Windows PC — read & reply to email, manage your calendar, text over iMessage, find files, work with Teams, Slack & Office. Runs locally, no API keys, free.",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -14,11 +14,12 @@
14
14
  "index.js",
15
15
  "download.js",
16
16
  "setup.js",
17
+ "launch-event.js",
17
18
  "postinstall.js",
18
19
  "README.md"
19
20
  ],
20
21
  "engines": {
21
- "node": ">=18"
22
+ "node": ">=20"
22
23
  },
23
24
  "os": [
24
25
  "darwin",
@@ -68,10 +69,10 @@
68
69
  "homepage": "https://local-mcp.com",
69
70
  "repository": {
70
71
  "type": "git",
71
- "url": "https://github.com/lanchuske/local-mcp-releases.git"
72
+ "url": "https://github.com/colibird-ai/local-mcp-releases.git"
72
73
  },
73
74
  "bugs": {
74
- "url": "https://github.com/lanchuske/local-mcp-releases/issues"
75
+ "url": "https://github.com/colibird-ai/local-mcp-releases/issues"
75
76
  },
76
77
  "mcpName": "com.local-mcp/local-mcp"
77
78
  }
package/setup.js CHANGED
@@ -237,11 +237,12 @@ function _launcherArgsForConfigureClients(platform, command, args) {
237
237
  return platform === 'darwin' ? { command, args } : { command: null, args: [] }
238
238
  }
239
239
 
240
- // §3.8 — Linux's fixed result shape: no capability, no report, exit 0 (not
241
- // having a writer yet is not a failure). Extracted so the contract itself is
242
- // directly testable instead of only reachable by reading runSetup's source.
240
+ // §3.8 — Linux's fixed result shape: no capability, no report, exit 1. It said exit 0 while
241
+ // Linux was "no writer yet"; since #9 Linux is not supported, and a setup that installed nothing
242
+ // is a failed setup: a script that chains on it has to see it. Extracted so the contract itself
243
+ // is directly testable instead of only reachable by reading runSetup's source.
243
244
  function _notSupportedOnPlatformResult() {
244
- return { runtime: 'not_supported_on_platform', exitCode: 0, report: null }
245
+ return { runtime: 'not_supported_on_platform', exitCode: 1, report: null }
245
246
  }
246
247
 
247
248
  // Corre `--configure-clients --json`. Nunca cuelga el instalador: un binario que no responde
@@ -296,10 +297,27 @@ async function runSetup(opts = {}) {
296
297
  // #3212 — `--json`: el reporte estructurado va solo por stdout; todo lo demás, a stderr.
297
298
  const jsonMode = !!opts.json || process.argv.includes('--json')
298
299
  if (jsonMode) console.log = (...a) => { process.stderr.write(a.join(' ') + '\n') }
299
- // Cross-platform: macOS configures the Swift tray + embedded server; Windows/Linux
300
- // download the go-server standalone binary (download.js) and write the MCP client
301
- // config (the config writer already handles Windows paths). The Mac-only waitlist
302
- // gate was retired for the Windows/Linux public re-launch.
300
+ // Linux no está soportado (#9): se dice y no se toca nada — sin descarga, sin launcher, sin
301
+ // eventos de instalación ni heartbeat (antes terminaba en «LMCP is installed and running»
302
+ // después de un 404). El reporte --json conserva su forma (§3.8); el código de salida es 1.
303
+ if (process.platform === 'linux') {
304
+ const sinLinux = _notSupportedOnPlatformResult()
305
+ console.log('\n ' + require('./download').LINUX_NO_SOPORTADO + '\n Nothing was installed.\n')
306
+ if (jsonMode) {
307
+ process.stdout.write(JSON.stringify({
308
+ schema: CLIENTS_REPORT_SCHEMA,
309
+ installer_version: require('./package.json').version,
310
+ runtime: sinLinux.runtime,
311
+ catalog: null,
312
+ clients: [],
313
+ permissions: 'unknown_until_tray_runs',
314
+ }) + '\n')
315
+ }
316
+ process.exitCode = sinLinux.exitCode
317
+ return
318
+ }
319
+ // Cross-platform: macOS configures the Swift tray + embedded server; Windows downloads the
320
+ // go-server standalone binary (download.js) and writes the MCP client config.
303
321
  const forceAll = opts.all || process.argv.includes('--all')
304
322
  const refArg = process.argv.find(a => a.startsWith('--ref='))
305
323
  if (refArg) process.env.LMCP_REF = refArg.split('=')[1]
@@ -469,16 +487,6 @@ async function runSetup(opts = {}) {
469
487
  console.log(' npx local-mcp setup\n')
470
488
  console.log(' Full setup guide: https://local-mcp.com/setup')
471
489
  }
472
- } else {
473
- // Linux (§3.8): no runtime-native config writer exists yet. Say so plainly,
474
- // point at the manual guide, and touch nothing — no per-client guessing,
475
- // no files written, no client identity hardcoded here.
476
- clientsRun = _notSupportedOnPlatformResult()
477
- _trackSetupStep('detect', 'not_supported_on_platform', '', { npmVersion: _npmPkgVersion })
478
- console.log('\n LMCP is installed and running.')
479
- console.log(' Automatic AI-client configuration is not available on Linux yet.')
480
- console.log(' Add LMCP to your AI client\'s MCP config manually — see:')
481
- console.log(' https://local-mcp.com/setup\n')
482
490
  }
483
491
 
484
492
  const cfgDir = _localMcpConfigDir()
@@ -530,7 +538,7 @@ async function runSetup(opts = {}) {
530
538
  if (configured.length > 0) {
531
539
  if (healthOk) {
532
540
  console.log(`✅ LMCP configured for: ${configured.join(', ')}\n`)
533
- console.log(' ✓ Server binary verified — 191+ tools ready\n')
541
+ console.log(' ✓ Server binary verified — 192+ tools ready\n')
534
542
  } else {
535
543
  console.log(`⚠ LMCP configured for: ${configured.join(', ')}`)
536
544
  console.log(' Server binary could not be verified — it may still work after restart.\n')
@@ -678,7 +686,7 @@ async function runSetup(opts = {}) {
678
686
 
679
687
  // #3212 — el reporte para quien instaló sin mirar (un agente): qué quedó hecho y qué espera
680
688
  // a la persona. Los permisos, sólo si los midió el tray. clientsRun is always set by this
681
- // point — darwin/win32 from runConfigureClients, linux from its own not_supported sentinel.
689
+ // point — darwin/win32 from runConfigureClients (linux returned at the top, #9).
682
690
  if (jsonMode) {
683
691
  const rep = clientsRun.report || {}
684
692
  const out = {