local-mcp 2.7.0 → 2.9.0
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/index.js +38 -41
- package/package.json +1 -1
- package/setup.js +8 -11
package/index.js
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* npx local-mcp status → muestra estado del servidor local
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
-
const { spawn, execFileSync } = require('child_process')
|
|
14
|
+
const { spawn, execFileSync, execSync } = require('child_process')
|
|
15
15
|
const path = require('path')
|
|
16
16
|
const os = require('os')
|
|
17
17
|
const fs = require('fs')
|
|
@@ -71,11 +71,37 @@ async function main() {
|
|
|
71
71
|
return
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
-
// Modo default: stdio MCP server
|
|
75
|
-
const {
|
|
74
|
+
// ── Modo default: stdio MCP server ──────────────────────────────────────────
|
|
75
|
+
const { CACHE_DIR } = require('./download')
|
|
76
|
+
const stableBin = path.join(CACHE_DIR, 'local-mcp-server')
|
|
76
77
|
|
|
77
|
-
//
|
|
78
|
-
|
|
78
|
+
// Fast path: if stable binary exists and is a real file (not a broken symlink), exec it directly.
|
|
79
|
+
// This makes npx startup near-instant when everything is healthy.
|
|
80
|
+
try {
|
|
81
|
+
const stat = fs.lstatSync(stableBin)
|
|
82
|
+
// Only use fast path if it's a real file (not a symlink — symlinks can break)
|
|
83
|
+
if (stat.isFile() && (stat.mode & 0o111)) {
|
|
84
|
+
// Download tray + teams-proxy in background (non-blocking)
|
|
85
|
+
const { ensureTray, ensureTeamsProxy } = require('./download')
|
|
86
|
+
ensureTray().catch(() => {})
|
|
87
|
+
ensureTeamsProxy().catch(() => {})
|
|
88
|
+
const child = spawn(stableBin, [], { stdio: 'inherit', env: process.env })
|
|
89
|
+
child.on('error', () => process.exit(1))
|
|
90
|
+
child.on('exit', (code) => process.exit(code ?? 0))
|
|
91
|
+
return
|
|
92
|
+
}
|
|
93
|
+
// If it's a symlink (legacy), remove it and fall through to download
|
|
94
|
+
if (stat.isSymbolicLink()) fs.unlinkSync(stableBin)
|
|
95
|
+
} catch {
|
|
96
|
+
// stat failed — binary missing, fall through to download
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Slow path: download/repair binary
|
|
100
|
+
const { ensureRuntime, ensureTray, ensureTeamsProxy } = require('./download')
|
|
101
|
+
|
|
102
|
+
// Actualizar tray + teams-proxy en background
|
|
103
|
+
ensureTray().catch(() => {})
|
|
104
|
+
ensureTeamsProxy().catch(() => {})
|
|
79
105
|
|
|
80
106
|
let runtime
|
|
81
107
|
try {
|
|
@@ -88,55 +114,26 @@ async function main() {
|
|
|
88
114
|
|
|
89
115
|
const { binPath } = runtime
|
|
90
116
|
|
|
91
|
-
//
|
|
92
|
-
// Copy binary to a stable path so auto-updates (which clean versioned dirs)
|
|
93
|
-
// never break the binary that Claude Desktop points to.
|
|
94
|
-
const { CACHE_DIR } = require('./download')
|
|
95
|
-
const stablePath = path.join(CACHE_DIR, 'local-mcp-server')
|
|
117
|
+
// Copy binary to stable path (survives version cleanup)
|
|
96
118
|
try {
|
|
97
|
-
const tmpPath =
|
|
119
|
+
const tmpPath = stableBin + '.tmp'
|
|
98
120
|
fs.copyFileSync(binPath, tmpPath)
|
|
99
121
|
fs.chmodSync(tmpPath, 0o755)
|
|
100
|
-
// Re-sign with stable identity
|
|
101
122
|
try { execFileSync('codesign', ['--force', '--sign', '-', '--identifier', 'com.local-mcp.server', tmpPath], { stdio: 'pipe' }) } catch {}
|
|
102
|
-
|
|
103
|
-
fs.renameSync(tmpPath, stablePath)
|
|
123
|
+
fs.renameSync(tmpPath, stableBin)
|
|
104
124
|
// Also copy settings.html
|
|
105
125
|
const settingsSrc = path.join(path.dirname(binPath), 'settings.html')
|
|
106
126
|
const settingsDst = path.join(CACHE_DIR, 'settings.html')
|
|
107
127
|
if (fs.existsSync(settingsSrc)) fs.copyFileSync(settingsSrc, settingsDst)
|
|
128
|
+
} catch {}
|
|
108
129
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
os.homedir(), 'Library', 'Application Support', 'Claude', 'claude_desktop_config.json'
|
|
112
|
-
)
|
|
113
|
-
if (fs.existsSync(claudeConfig)) {
|
|
114
|
-
const cfg = JSON.parse(fs.readFileSync(claudeConfig, 'utf8'))
|
|
115
|
-
const srv = cfg.mcpServers?.['local-mcp']
|
|
116
|
-
if (srv && srv.command === 'npx') {
|
|
117
|
-
cfg.mcpServers['local-mcp'] = { command: stablePath }
|
|
118
|
-
fs.writeFileSync(claudeConfig, JSON.stringify(cfg, null, 2))
|
|
119
|
-
process.stderr.write(`\n✓ Config de Claude Desktop actualizado.\n`)
|
|
120
|
-
process.stderr.write(` Reiniciá Claude Desktop para que Pilot MCP corra sin node como padre.\n`)
|
|
121
|
-
process.stderr.write(` (Esto elimina los diálogos de permisos repetidos.)\n\n`)
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
} catch { /* no crítico — seguimos con spawn normal */ }
|
|
125
|
-
|
|
126
|
-
// Spawn para esta sesión (siguiente sesión ya será binario directo)
|
|
127
|
-
const child = spawn(binPath, [], {
|
|
128
|
-
stdio: 'inherit',
|
|
129
|
-
env: process.env,
|
|
130
|
-
})
|
|
131
|
-
|
|
130
|
+
// Run the binary
|
|
131
|
+
const child = spawn(binPath, [], { stdio: 'inherit', env: process.env })
|
|
132
132
|
child.on('error', (err) => {
|
|
133
133
|
process.stderr.write(`Error ejecutando Pilot MCP: ${err.message}\n`)
|
|
134
134
|
process.exit(1)
|
|
135
135
|
})
|
|
136
|
-
|
|
137
|
-
child.on('exit', (code) => {
|
|
138
|
-
process.exit(code ?? 0)
|
|
139
|
-
})
|
|
136
|
+
child.on('exit', (code) => process.exit(code ?? 0))
|
|
140
137
|
}
|
|
141
138
|
|
|
142
139
|
main().catch(err => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "local-mcp",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.9.0",
|
|
4
4
|
"description": "Pilot MCP — 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
|
@@ -112,35 +112,32 @@ async function runSetup(opts = {}) {
|
|
|
112
112
|
console.log('║ Pilot MCP — Setup Wizard ║')
|
|
113
113
|
console.log('╚══════════════════════════════════════╝\n')
|
|
114
114
|
|
|
115
|
-
//
|
|
116
|
-
//
|
|
115
|
+
// Pre-download binary so first launch is instant
|
|
116
|
+
// All clients use npx as launcher (self-healing if binary is missing/broken)
|
|
117
117
|
let stableCommand = NPX_COMMAND
|
|
118
118
|
let stableArgs = NPX_ARGS
|
|
119
119
|
try {
|
|
120
120
|
const { ensureBinary, CACHE_DIR } = require('./download')
|
|
121
121
|
process.stderr.write('Downloading Pilot MCP runtime...\n')
|
|
122
122
|
const { binPath } = await ensureBinary()
|
|
123
|
-
// Copy binary to stable path (
|
|
123
|
+
// Copy binary to stable path (npx fast-path will exec it directly)
|
|
124
124
|
const stablePath = path.join(CACHE_DIR, 'local-mcp-server')
|
|
125
125
|
try {
|
|
126
126
|
const tmpPath = stablePath + '.tmp'
|
|
127
127
|
fs.copyFileSync(binPath, tmpPath)
|
|
128
128
|
fs.chmodSync(tmpPath, 0o755)
|
|
129
|
-
try {
|
|
129
|
+
try { execFileSync('codesign', ['--force', '--sign', '-', '--identifier', 'com.local-mcp.server', tmpPath], { stdio: 'pipe' }) } catch {}
|
|
130
130
|
fs.renameSync(tmpPath, stablePath)
|
|
131
|
-
// Also copy settings.html
|
|
132
131
|
const settingsSrc = path.join(path.dirname(binPath), 'settings.html')
|
|
133
132
|
const settingsDst = path.join(CACHE_DIR, 'settings.html')
|
|
134
133
|
if (fs.existsSync(settingsSrc)) fs.copyFileSync(settingsSrc, settingsDst)
|
|
135
|
-
} catch {
|
|
136
|
-
|
|
137
|
-
stableCommand = stablePath
|
|
138
|
-
stableArgs = undefined
|
|
139
|
-
process.stderr.write('✓ Runtime ready\n\n')
|
|
140
|
-
}
|
|
134
|
+
} catch {}
|
|
135
|
+
process.stderr.write('✓ Runtime ready\n\n')
|
|
141
136
|
} catch (err) {
|
|
142
137
|
process.stderr.write(` (Runtime download failed, will download on first run: ${err.message})\n\n`)
|
|
143
138
|
}
|
|
139
|
+
// Always use npx as the command — it has a fast-path that execs the binary directly
|
|
140
|
+
// but can self-heal if the binary is missing or broken
|
|
144
141
|
|
|
145
142
|
// Detectar clientes
|
|
146
143
|
const detected = CLIENTS.filter(c => c.detect())
|