local-mcp 3.0.370 → 3.0.372

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,6 +1,6 @@
1
1
  # Local MCP
2
2
 
3
- > **The only Mac MCP you can use from ChatGPT & Claude.ai on the web** — plus 271 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, Safari, Chrome, Word/Excel/PowerPoint and more. 100% local, no API keys, free — your data never leaves your machine.
3
+ > **The only Mac MCP you can use from ChatGPT & Claude.ai on the web** — plus 215+ 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, Safari, Chrome, Word/Excel/PowerPoint and more. 100% local, no API keys, free — your data never leaves your machine.
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)
@@ -74,7 +74,7 @@ LMCP runs a native MCP server that bridges your Mac apps to any AI client:
74
74
 
75
75
   
76
76
 
77
- ## All 271 tools (macOS)
77
+ ## Tool catalog (macOS)
78
78
 
79
79
  Email (12): `list_accounts` `list_email_accounts` `list_emails` `read_email` `send_email` `reply_email` `create_draft` `search_emails` `move_email` `save_attachment` `create_email_folder` `list_email_folders`
80
80
 
package/download.js CHANGED
@@ -18,6 +18,15 @@ function extractTar(tarPath, destDir) {
18
18
  const tarBin = process.platform === 'win32'
19
19
  ? path.join(process.env.SystemRoot || 'C:\\Windows', 'System32', 'tar.exe')
20
20
  : 'tar'
21
+ // Windows 10 1803+ ships bsdtar at System32\tar.exe. On an older/stripped Windows
22
+ // it's absent — fail with a clear cause instead of a bare execFileSync ENOENT that
23
+ // the caller would otherwise mask as "not yet available" (#1256).
24
+ if (process.platform === 'win32' && !fs.existsSync(tarBin)) {
25
+ throw new Error(
26
+ `tar.exe not found at ${tarBin} — Windows 10 1803+ (with built-in bsdtar) is ` +
27
+ `required to unpack the LMCP server.`
28
+ )
29
+ }
21
30
  execFileSync(tarBin, ['-xzf', tarPath, '-C', destDir], { stdio: 'pipe' })
22
31
  }
23
32
 
@@ -122,7 +131,7 @@ async function getLatestBinary() {
122
131
  /**
123
132
  * Descarga un archivo con barra de progreso.
124
133
  */
125
- async function downloadFile(url, destPath) {
134
+ function downloadOnce(url, destPath) {
126
135
  return new Promise((resolve, reject) => {
127
136
  const file = fs.createWriteStream(destPath)
128
137
  const proto = url.startsWith('https') ? https : http
@@ -164,6 +173,27 @@ async function downloadFile(url, destPath) {
164
173
  })
165
174
  }
166
175
 
176
+ // Retry transient download failures (timeouts, resets, 5xx). A 4xx is permanent —
177
+ // a missing/forbidden artifact won't appear on a retry, so fail fast and let the
178
+ // caller decide whether that means "not yet published". Backoff grows per attempt.
179
+ async function downloadFile(url, destPath) {
180
+ const maxAttempts = 3
181
+ let lastErr
182
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
183
+ try {
184
+ return await downloadOnce(url, destPath)
185
+ } catch (err) {
186
+ lastErr = err
187
+ if (/HTTP 4\d\d/.test(err.message)) throw err // permanent, don't retry
188
+ if (attempt < maxAttempts) {
189
+ process.stderr.write(`\n Download failed (${err.message}) — retrying (${attempt}/${maxAttempts - 1})...\n`)
190
+ await new Promise(r => setTimeout(r, 1000 * attempt))
191
+ }
192
+ }
193
+ }
194
+ throw lastErr
195
+ }
196
+
167
197
  /**
168
198
  * Asegura que el binario esté descargado y listo.
169
199
  * @returns {Promise<{binPath: string}>}
@@ -233,11 +263,20 @@ async function ensureBinary() {
233
263
  extractTar(tarPath, CACHE_DIR)
234
264
  fs.unlinkSync(tarPath)
235
265
  } catch (err) {
236
- // Go binary not yet published for this version — fall back to message
237
266
  try { fs.unlinkSync(tarPath) } catch {}
267
+ // Only a 404 means the artifact for this version/arch genuinely isn't published.
268
+ // Every other failure (network, missing tar.exe, disk) is an environment problem —
269
+ // surface it verbatim so it's diagnosable. Masking ALL failures as "in preview" sent
270
+ // us chasing a backend version-alignment ghost that didn't exist (#1256): the artifact
271
+ // was published, valid and hash-correct; the client just failed to fetch/unpack it.
272
+ if (/HTTP 404/.test(err.message)) {
273
+ throw new Error(
274
+ `LMCP ${platformVersion} is not yet available for ${arch}.\n` +
275
+ `The Windows/Linux Go server is in preview — check https://local-mcp.com for updates.`
276
+ )
277
+ }
238
278
  throw new Error(
239
- `LMCP ${platformVersion} is not yet available for ${arch}.\n` +
240
- `The Windows/Linux Go server is in preview — check https://local-mcp.com for updates.`
279
+ `Failed to install LMCP ${platformVersion} (${arch}) from ${url}:\n ${err.message}`
241
280
  )
242
281
  }
243
282
 
@@ -360,9 +399,12 @@ async function ensureTeamsProxy() {
360
399
  * @returns {Promise<string|null>} Ruta al .app instalado, o null si falla
361
400
  */
362
401
  async function ensureTray() {
363
- // Windows: download lmcp-tray.exe
364
- if (process.platform === 'win32') {
365
- return _ensureTrayWindows()
402
+ // Windows/Linux: the tray + background daemon are installed by the native
403
+ // installer (LMCP-Setup.exe on Windows; the systemd unit on Linux), NOT by npm.
404
+ // The npm package is a stdio proxy for MCP hosts, so there is no tray to fetch
405
+ // here — no-op instead of downloading a lmcp-tray binary.
406
+ if (process.platform !== 'darwin') {
407
+ return null
366
408
  }
367
409
 
368
410
  // Respect explicit uninstall — don't resurrect tray if user ran `uninstall`.
package/index.js CHANGED
@@ -51,10 +51,12 @@ function clearUninstallSentinels() {
51
51
  for (const p of uninstallSentinelPaths()) { try { fs.unlinkSync(p) } catch {} }
52
52
  }
53
53
 
54
- // Platform support: macOS only (Windows/Linux on waitlist at local-mcp.com)
55
- const SUPPORTED_PLATFORMS = ['darwin']
54
+ // Platform support: macOS (Swift tray + embedded server), Windows & Linux
55
+ // (go-server standalone binary downloaded from R2). Re-enabled for the
56
+ // Windows/Linux public re-launch — the waitlist gate is retired.
57
+ const SUPPORTED_PLATFORMS = ['darwin', 'win32', 'linux']
56
58
  if (!SUPPORTED_PLATFORMS.includes(process.platform)) {
57
- console.error(`LMCP is currently macOS-only. Join the Windows/Linux waitlist at https://local-mcp.com`)
59
+ console.error(`LMCP supports macOS, Windows and Linux. Your platform (${process.platform}) is not supported yet — see https://local-mcp.com`)
58
60
  process.exit(1)
59
61
  }
60
62
 
@@ -222,7 +224,12 @@ async function main() {
222
224
 
223
225
  // ── Modo default: stdio MCP server ──────────────────────────────────────────
224
226
  const { CACHE_DIR } = require('./download')
225
- const binName = process.platform === 'win32' ? 'local-mcp-server.exe' : 'local-mcp-server'
227
+ // macOS ships the Swift `local-mcp-server` (standalone fallback to the embedded
228
+ // tray server); Windows/Linux ship the go-server `lmcp-server` binary that
229
+ // download.js writes to CACHE_DIR. Keep this name in sync with download.js.
230
+ const binName = process.platform === 'darwin'
231
+ ? 'local-mcp-server'
232
+ : (process.platform === 'win32' ? 'lmcp-server.exe' : 'lmcp-server')
226
233
  const stableBin = path.join(CACHE_DIR, binName)
227
234
  const versionFile = path.join(CACHE_DIR, '.server-version')
228
235
  const pkg = require('./package.json')
@@ -365,7 +372,12 @@ async function main() {
365
372
  // because stale npx cache had old pkg.version and triggered the slow path unnecessarily.
366
373
  try {
367
374
  const stat = fs.lstatSync(stableBin)
368
- if (stat.isFile() && (stat.mode & 0o111)) {
375
+ // Windows .exe files carry no Unix exec bit — Node's fs reports mode 0o666
376
+ // so the `& 0o111` gate is always false there, which forced EVERY spawn onto
377
+ // the slow path (a /runtime/latest network round-trip before launch, and a
378
+ // hard failure when offline despite a valid cached binary). Only require the
379
+ // exec bit off-Windows, where it's meaningful.
380
+ if (stat.isFile() && (process.platform === 'win32' || (stat.mode & 0o111))) {
369
381
  const cachedVersion = fs.existsSync(versionFile)
370
382
  ? fs.readFileSync(versionFile, 'utf8').trim()
371
383
  : ''
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "local-mcp",
3
- "version": "3.0.370",
3
+ "version": "3.0.372",
4
4
  "description": "Let ChatGPT, Claude, Cursor & any MCP client actually use your Mac — read & reply to email, manage your calendar, text over iMessage, find files, work with Teams, Slack & Office. On your Mac, no API keys, free.",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -20,7 +20,9 @@
20
20
  "node": ">=18"
21
21
  },
22
22
  "os": [
23
- "darwin"
23
+ "darwin",
24
+ "linux",
25
+ "win32"
24
26
  ],
25
27
  "keywords": [
26
28
  "mcp",
package/postinstall.js CHANGED
@@ -9,11 +9,6 @@ if (!process.env.npm_config_global || process.env.CI || process.env.SKIP_SETUP)
9
9
  process.exit(0)
10
10
  }
11
11
 
12
- // Solo macOS
13
- if (process.platform !== 'darwin') {
14
- process.exit(0)
15
- }
16
-
17
12
  const { runSetup } = require('./setup')
18
13
 
19
14
  runSetup({ all: true }).catch(err => {
package/setup.js CHANGED
@@ -331,23 +331,10 @@ function injectMcpConfig(client, command = NPX_COMMAND, args = NPX_ARGS) {
331
331
  // ── Main ──────────────────────────────────────────────────────────────────────
332
332
 
333
333
  async function runSetup(opts = {}) {
334
- // macOS only. `npx local-mcp setup` on Windows/Linux used to silently download +
335
- // configure the (still-present) Go server, bypassing the Mac-only waitlist that the
336
- // landing page and cloud connector enforce. Gate it here so new non-mac installs get
337
- // the waitlist message instead of a working-but-unsupported install. (The legacy
338
- // Windows R2 binaries stay so the existing fleet keeps auto-updating.)
339
- if (process.platform !== 'darwin') {
340
- const osName = process.platform === 'win32' ? 'Windows' : process.platform === 'linux' ? 'Linux' : process.platform
341
- console.log(`\n LMCP is macOS-only right now — ${osName} isn't supported yet.`)
342
- console.log(' Join the waitlist to be notified when it ships: https://local-mcp.com\n')
343
- try { // best-effort: record the gated attempt so we can size non-mac demand
344
- const https = require('https')
345
- const req = https.request({ hostname: BACKEND_HOST, path: '/install/started/npm-gated-' + process.platform, method: 'POST', timeout: 2500 })
346
- req.on('error', () => {}); req.end()
347
- } catch {}
348
- return
349
- }
350
-
334
+ // Cross-platform: macOS configures the Swift tray + embedded server; Windows/Linux
335
+ // download the go-server standalone binary (download.js) and write the MCP client
336
+ // config (the config writer already handles Windows paths). The Mac-only waitlist
337
+ // gate was retired for the Windows/Linux public re-launch.
351
338
  const forceAll = opts.all || process.argv.includes('--all')
352
339
  const refArg = process.argv.find(a => a.startsWith('--ref='))
353
340
  if (refArg) process.env.LMCP_REF = refArg.split('=')[1]
@@ -584,7 +571,7 @@ async function runSetup(opts = {}) {
584
571
  if (configured.length > 0) {
585
572
  if (healthOk) {
586
573
  console.log(`✅ LMCP configured for: ${configured.join(', ')}\n`)
587
- console.log(' ✓ Server binary verified — 100 tools ready\n')
574
+ console.log(' ✓ Server binary verified — 215+ tools ready\n')
588
575
  } else {
589
576
  console.log(`⚠ LMCP configured for: ${configured.join(', ')}`)
590
577
  console.log(' Server binary could not be verified — it may still work after restart.\n')