local-mcp 3.0.219 → 3.0.221
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/download.js +3 -0
- package/index.js +72 -30
- package/package.json +1 -1
package/download.js
CHANGED
|
@@ -393,6 +393,9 @@ async function ensureTray() {
|
|
|
393
393
|
try { execFileSync('xattr', ['-rd', 'com.apple.quarantine', trayApp], { stdio: 'pipe' }) } catch { /* no crítico */ }
|
|
394
394
|
|
|
395
395
|
fs.writeFileSync(verFile, version, 'utf8')
|
|
396
|
+
// Keep in sync with UpdateManager / Heartbeat (was .version only → fleet stuck on old tray)
|
|
397
|
+
const trayVerFile = path.join(TRAY_DIR, '.tray-version')
|
|
398
|
+
fs.writeFileSync(trayVerFile, version, 'utf8')
|
|
396
399
|
|
|
397
400
|
// LaunchAgent — auto-start en cada reboot de sesión
|
|
398
401
|
_writeTrayLaunchAgent(trayApp)
|
package/index.js
CHANGED
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
* index.js — entry point del paquete npm local-mcp.
|
|
5
5
|
*
|
|
6
6
|
* Modos:
|
|
7
|
-
* npx local-mcp →
|
|
7
|
+
* npx local-mcp → instala/actualiza runtime; en terminal interactiva muestra
|
|
8
|
+
* mensaje de listo y sale (Cursor/Claude lanzan stdio solos)
|
|
8
9
|
* npx local-mcp setup → wizard de configuración de clientes
|
|
9
10
|
* npx local-mcp update → fuerza re-descarga del runtime
|
|
10
11
|
* npx local-mcp uninstall → elimina cache del runtime
|
|
@@ -139,6 +140,50 @@ async function main() {
|
|
|
139
140
|
return true
|
|
140
141
|
}
|
|
141
142
|
|
|
143
|
+
/** MCP hosts (Cursor, Claude) spawn us without a TTY — keep stdio bridge alive. */
|
|
144
|
+
const isMcpClientSpawn = !(process.stdin && process.stdin.isTTY)
|
|
145
|
+
|
|
146
|
+
function printInstallComplete(version) {
|
|
147
|
+
process.stderr.write('\n')
|
|
148
|
+
process.stderr.write(`✓ Local MCP v${version} — installation complete.\n\n`)
|
|
149
|
+
process.stderr.write(' • Menu bar: open LocalMCPTray from Applications (look for the LMCP icon).\n')
|
|
150
|
+
process.stderr.write(' • Cursor / Claude: enable the "local-mcp" MCP server in settings (they start it automatically).\n')
|
|
151
|
+
process.stderr.write(' • You can close this terminal — do not leave `npx local-mcp` running here.\n')
|
|
152
|
+
process.stderr.write(' • Status: http://localhost:8765/status\n\n')
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
async function ensureDarwinComponents() {
|
|
156
|
+
if (process.platform !== 'darwin') return
|
|
157
|
+
const { ensureTray, ensureTeamsProxy, ensureHelper, ensureJXARunner } = require('./download')
|
|
158
|
+
await Promise.all([
|
|
159
|
+
ensureTray().catch((e) => { process.stderr.write(`[LMCP] Tray install failed: ${e.message}\n`) }),
|
|
160
|
+
ensureTeamsProxy().catch((e) => { process.stderr.write(`[LMCP] teams-proxy install failed: ${e.message}\n`) }),
|
|
161
|
+
ensureHelper().catch((e) => { process.stderr.write(`[LMCP] helper install failed: ${e.message}\n`) }),
|
|
162
|
+
ensureJXARunner().catch((e) => { process.stderr.write(`[LMCP] jxa-runner install failed: ${e.message}\n`) }),
|
|
163
|
+
])
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function launchMcpStdio(binaryPath, version) {
|
|
167
|
+
if (!isMcpClientSpawn) {
|
|
168
|
+
printInstallComplete(version)
|
|
169
|
+
if (process.platform === 'darwin') {
|
|
170
|
+
const home = os.homedir()
|
|
171
|
+
const tray = path.join(home, 'Applications', 'LocalMCPTray.app')
|
|
172
|
+
const trayPath = fs.existsSync(tray) ? tray : '/Applications/LocalMCPTray.app'
|
|
173
|
+
try { execFileSync('open', ['-a', trayPath], { stdio: 'ignore' }) } catch {}
|
|
174
|
+
}
|
|
175
|
+
process.exit(0)
|
|
176
|
+
return
|
|
177
|
+
}
|
|
178
|
+
const spawnArgs = process.platform === 'darwin' ? ['stdio'] : []
|
|
179
|
+
const child = spawn(binaryPath, spawnArgs, { stdio: 'inherit', env: process.env })
|
|
180
|
+
child.on('error', (err) => {
|
|
181
|
+
process.stderr.write(`Error ejecutando LMCP: ${err.message}\n`)
|
|
182
|
+
process.exit(1)
|
|
183
|
+
})
|
|
184
|
+
child.on('exit', (code) => process.exit(code ?? 0))
|
|
185
|
+
}
|
|
186
|
+
|
|
142
187
|
// On Windows: apply pending update before launching (same as Mac's UpdateManager)
|
|
143
188
|
// The Go server downloads new versions in background and stages them as .pending.
|
|
144
189
|
// On next Claude Desktop restart, we swap them in here.
|
|
@@ -178,17 +223,8 @@ async function main() {
|
|
|
178
223
|
: ''
|
|
179
224
|
if (cachedVersion && semverGte(cachedVersion, pkg.version)) {
|
|
180
225
|
// Cached binary is same or newer — use fast path, no download needed
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
ensureTray().catch((e) => { process.stderr.write(`[LMCP] Tray install failed: ${e.message}\n`) })
|
|
184
|
-
ensureTeamsProxy().catch((e) => { process.stderr.write(`[LMCP] teams-proxy install failed: ${e.message}\n`) })
|
|
185
|
-
ensureHelper().catch((e) => { process.stderr.write(`[LMCP] helper install failed: ${e.message}\n`) })
|
|
186
|
-
ensureJXARunner().catch((e) => { process.stderr.write(`[LMCP] jxa-runner install failed: ${e.message}\n`) })
|
|
187
|
-
}
|
|
188
|
-
const args = process.platform === 'darwin' ? ['stdio'] : []
|
|
189
|
-
const child = spawn(stableBin, args, { stdio: 'inherit', env: process.env })
|
|
190
|
-
child.on('error', () => process.exit(1))
|
|
191
|
-
child.on('exit', (code) => process.exit(code ?? 0))
|
|
226
|
+
await ensureDarwinComponents()
|
|
227
|
+
launchMcpStdio(stableBin, cachedVersion || pkg.version)
|
|
192
228
|
|
|
193
229
|
// Background update check — fires 10s after startup, at most once per 24h.
|
|
194
230
|
// Covers machines where the tray is dead and Sparkle can't update the binary.
|
|
@@ -207,10 +243,30 @@ async function main() {
|
|
|
207
243
|
try {
|
|
208
244
|
const { version: latestVersion } = JSON.parse(buf)
|
|
209
245
|
if (latestVersion && !semverGte(_cachedVersion, latestVersion)) {
|
|
210
|
-
// New version available — download
|
|
246
|
+
// New version available — download for next restart only while no server is running.
|
|
247
|
+
// Replacing stableBin mid-session changes its mtime and used to cause sibling
|
|
248
|
+
// stdio proxies to be SIGTERM'd → Cursor transport_closed (LMCA-1044).
|
|
249
|
+
let activeServers = 0
|
|
250
|
+
try {
|
|
251
|
+
activeServers = parseInt(
|
|
252
|
+
execSync('pgrep -fc local-mcp-server 2>/dev/null || echo 0', { encoding: 'utf8' }).trim(),
|
|
253
|
+
10
|
|
254
|
+
) || 0
|
|
255
|
+
} catch {}
|
|
256
|
+
if (activeServers > 0) return
|
|
257
|
+
|
|
211
258
|
const { ensureRuntime } = require('./download')
|
|
212
259
|
ensureRuntime().then(({ binPath, version: downloadedVersion }) => {
|
|
213
260
|
try {
|
|
261
|
+
let stillActive = 0
|
|
262
|
+
try {
|
|
263
|
+
stillActive = parseInt(
|
|
264
|
+
execSync('pgrep -fc local-mcp-server 2>/dev/null || echo 0', { encoding: 'utf8' }).trim(),
|
|
265
|
+
10
|
|
266
|
+
) || 0
|
|
267
|
+
} catch {}
|
|
268
|
+
if (stillActive > 0) return
|
|
269
|
+
|
|
214
270
|
const tmpPath = stableBin + '.tmp'
|
|
215
271
|
fs.copyFileSync(binPath, tmpPath)
|
|
216
272
|
fs.chmodSync(tmpPath, 0o755)
|
|
@@ -241,15 +297,7 @@ async function main() {
|
|
|
241
297
|
}
|
|
242
298
|
|
|
243
299
|
// Slow path: download/repair binary
|
|
244
|
-
const { ensureRuntime
|
|
245
|
-
|
|
246
|
-
// Update tray + proxies in background (macOS only).
|
|
247
|
-
if (process.platform === 'darwin') {
|
|
248
|
-
ensureTray().catch((e) => { process.stderr.write(`[LMCP] Tray install failed: ${e.message}\n`) })
|
|
249
|
-
ensureTeamsProxy().catch((e) => { process.stderr.write(`[LMCP] teams-proxy install failed: ${e.message}\n`) })
|
|
250
|
-
ensureHelper().catch((e) => { process.stderr.write(`[LMCP] helper install failed: ${e.message}\n`) })
|
|
251
|
-
ensureJXARunner().catch((e) => { process.stderr.write(`[LMCP] jxa-runner install failed: ${e.message}\n`) })
|
|
252
|
-
}
|
|
300
|
+
const { ensureRuntime } = require('./download')
|
|
253
301
|
|
|
254
302
|
let runtime
|
|
255
303
|
try {
|
|
@@ -281,14 +329,8 @@ async function main() {
|
|
|
281
329
|
if (fs.existsSync(settingsSrc)) fs.copyFileSync(settingsSrc, settingsDst)
|
|
282
330
|
} catch {}
|
|
283
331
|
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
const child = spawn(binPath, spawnArgs, { stdio: 'inherit', env: process.env })
|
|
287
|
-
child.on('error', (err) => {
|
|
288
|
-
process.stderr.write(`Error ejecutando LMCP: ${err.message}\n`)
|
|
289
|
-
process.exit(1)
|
|
290
|
-
})
|
|
291
|
-
child.on('exit', (code) => process.exit(code ?? 0))
|
|
332
|
+
await ensureDarwinComponents()
|
|
333
|
+
launchMcpStdio(stableBin, downloadedVersion || pkg.version)
|
|
292
334
|
}
|
|
293
335
|
|
|
294
336
|
main().catch(err => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "local-mcp",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.221",
|
|
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": {
|