local-mcp 3.0.139 → 3.0.140

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/setup.js +68 -7
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "local-mcp",
3
- "version": "3.0.139",
3
+ "version": "3.0.140",
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
@@ -34,6 +34,19 @@ function _resolveNpxPath() {
34
34
  // BEFORE Node loads our code (e.g. old nvm default in Claude Desktop's PATH).
35
35
  // Returns the path to the launcher, or null if writing fails.
36
36
  function _writeLaunchScript(npxAbsPath, cacheDir) {
37
+ // On Windows, don't write a bash launch script — use npx directly
38
+ if (_IS_WIN) {
39
+ try {
40
+ // Write a .cmd wrapper that Claude Desktop can execute
41
+ fs.mkdirSync(cacheDir, { recursive: true })
42
+ const cmdPath = path.join(cacheDir, 'lmcp-launch.cmd')
43
+ const script = `@echo off\r\n"${npxAbsPath}" -y local-mcp@latest %*\r\n`
44
+ fs.writeFileSync(cmdPath, script)
45
+ return cmdPath
46
+ } catch {
47
+ return null
48
+ }
49
+ }
37
50
  try {
38
51
  fs.mkdirSync(cacheDir, { recursive: true })
39
52
  const launcherPath = path.join(cacheDir, 'lmcp-launch.sh')
@@ -71,13 +84,49 @@ const _IS_WIN = process.platform === 'win32'
71
84
  const _IS_MAC = process.platform === 'darwin'
72
85
  const _APPDATA = process.env.APPDATA || path.join(HOME, 'AppData', 'Roaming')
73
86
 
87
+ // Windows MSIX (Microsoft Store / WinGet) installs read config from a
88
+ // virtualized path inside %LOCALAPPDATA%\Packages\Claude_*\LocalCache\Roaming\Claude.
89
+ // The non-MSIX (.exe) install reads from %APPDATA%\Claude.
90
+ // We detect which one exists and write to both if needed.
91
+ function _findMsixClaudePath() {
92
+ if (!_IS_WIN) return null
93
+ const localAppData = process.env.LOCALAPPDATA || path.join(HOME, 'AppData', 'Local')
94
+ const packagesDir = path.join(localAppData, 'Packages')
95
+ try {
96
+ const entries = fs.readdirSync(packagesDir)
97
+ const claudePkg = entries.find(e => e.startsWith('Claude_'))
98
+ if (claudePkg) {
99
+ return path.join(packagesDir, claudePkg, 'LocalCache', 'Roaming', 'Claude', 'claude_desktop_config.json')
100
+ }
101
+ } catch {}
102
+ return null
103
+ }
104
+
74
105
  function _claudeDesktopPath() {
75
- if (_IS_WIN) return path.join(_APPDATA, 'Claude', 'claude_desktop_config.json')
106
+ if (_IS_WIN) {
107
+ // Prefer MSIX path if the package exists
108
+ const msix = _findMsixClaudePath()
109
+ if (msix) return msix
110
+ return path.join(_APPDATA, 'Claude', 'claude_desktop_config.json')
111
+ }
76
112
  if (_IS_MAC) return path.join(HOME, 'Library', 'Application Support', 'Claude', 'claude_desktop_config.json')
77
113
  return path.join(HOME, '.config', 'Claude', 'claude_desktop_config.json')
78
114
  }
79
115
 
116
+ // All paths where we should write the config (MSIX + non-MSIX on Windows)
117
+ function _claudeDesktopAllPaths() {
118
+ if (!_IS_WIN) return [_claudeDesktopPath()]
119
+ const paths = [path.join(_APPDATA, 'Claude', 'claude_desktop_config.json')]
120
+ const msix = _findMsixClaudePath()
121
+ if (msix && !paths.includes(msix)) paths.push(msix)
122
+ return paths
123
+ }
124
+
80
125
  function _claudeDesktopDetect() {
126
+ if (_IS_WIN) {
127
+ // Check both MSIX and non-MSIX paths
128
+ return _claudeDesktopAllPaths().some(p => fs.existsSync(path.dirname(p)))
129
+ }
81
130
  return fs.existsSync(path.dirname(_claudeDesktopPath()))
82
131
  }
83
132
 
@@ -221,6 +270,11 @@ function _atomicWriteConfig(filePath, data) {
221
270
  // Never overwrites a file whose JSON cannot be parsed.
222
271
 
223
272
  function injectMcpConfig(client, command = NPX_COMMAND, args = NPX_ARGS) {
273
+ // On Windows, Claude Desktop needs cmd /c wrapper to find npx
274
+ if (_IS_WIN && client.id === 'claude-desktop') {
275
+ command = 'cmd'
276
+ args = ['/c', 'npx', '-y', 'local-mcp@latest']
277
+ }
224
278
  // 1. Read existing config safely
225
279
  const read = _safeReadConfig(client.cfgPath)
226
280
  if (read.hadParseError) {
@@ -253,7 +307,14 @@ function injectMcpConfig(client, command = NPX_COMMAND, args = NPX_ARGS) {
253
307
  }
254
308
 
255
309
  // 3. Atomic write + verify
256
- const write = _atomicWriteConfig(client.cfgPath, cfg)
310
+ // On Windows, Claude Desktop MSIX reads from a different path than the .exe install.
311
+ // Write to all known paths so both install types work.
312
+ const allPaths = (client.id === 'claude-desktop' && _IS_WIN) ? _claudeDesktopAllPaths() : [client.cfgPath]
313
+ let write = { ok: false, error: 'no paths' }
314
+ for (const p of allPaths) {
315
+ fs.mkdirSync(path.dirname(p), { recursive: true })
316
+ write = _atomicWriteConfig(p, cfg)
317
+ }
257
318
  return {
258
319
  ok: write.ok,
259
320
  stage: write.ok ? 'config_write' : 'config_write_failed',
@@ -530,7 +591,7 @@ async function runSetup(opts = {}) {
530
591
  console.log('┌─────────────────────────────────────────────────────┐')
531
592
  console.log('│ CURSOR — activate in 3 steps: │')
532
593
  console.log('│ │')
533
- console.log('│ 1. Quit Cursor completely (Cmd+Q) ')
594
+ console.log(`│ 1. Quit Cursor completely (${_IS_WIN ? 'Alt+F4' : 'Cmd+Q'})${_IS_WIN ? ' ' : ' '}│`)
534
595
  console.log('│ 2. Reopen Cursor │')
535
596
  console.log('│ 3. Cursor Settings (⚙) → MCP → find "local-mcp" │')
536
597
  console.log('│ → toggle it ON │')
@@ -547,7 +608,8 @@ async function runSetup(opts = {}) {
547
608
  console.log('┌─────────────────────────────────────────────────────┐')
548
609
  console.log('│ NEXT STEP — restart to activate: │')
549
610
  console.log('│ │')
550
- console.log(`│ 1. Quit ${primaryClient} completely (Cmd+Q)${''.padEnd(Math.max(0, 30 - primaryClient.length))}│`)
611
+ const quitKey = _IS_WIN ? 'Alt+F4' : 'Cmd+Q'
612
+ console.log(`│ 1. Quit ${primaryClient} completely (${quitKey})${''.padEnd(Math.max(0, 28 - primaryClient.length - quitKey.length))}│`)
551
613
  console.log(`│ 2. Reopen ${primaryClient.padEnd(41)}│`)
552
614
  console.log('│ │')
553
615
  console.log('│ Then try: "Summarize my unread emails" │')
@@ -628,15 +690,14 @@ async function _installSlackProxy() {
628
690
  }
629
691
 
630
692
  async function _installTray() {
693
+ // Tray is macOS-only — skip on Windows/Linux
694
+ if (_IS_WIN || process.platform === 'linux') return
631
695
  try {
632
696
  const { ensureTray } = require('./download')
633
697
  const trayApp = await ensureTray()
634
698
  if (!trayApp) return // x64 — skip silencioso
635
699
  console.log('\n✓ Tray installed — look for the LMCP icon in your menu bar\n')
636
- // ensureTray() ya escribió el LaunchAgent y lo cargó con RunAtLoad=true
637
- // No hace falta llamar open() por separado
638
700
  } catch (err) {
639
- // No fatal — el servidor MCP funciona igual sin el tray
640
701
  process.stderr.write(` (Tray not available: ${err.message})\n\n`)
641
702
  }
642
703
  }