@morphllm/morphmcp 0.8.42 → 0.8.43
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/dist/index.js +164 -23
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -80,26 +80,21 @@ if (args.length === 0 && !ENABLE_WORKSPACE_MODE) {
|
|
|
80
80
|
// =============================================================================
|
|
81
81
|
const AUTO_UPDATE_INTERVAL_MS = parseInt(process.env.AUTO_UPDATE_INTERVAL_MS || '1800000', 10); // Default: 30 minutes
|
|
82
82
|
const DISABLE_AUTO_UPDATE = process.env.DISABLE_AUTO_UPDATE === 'true';
|
|
83
|
+
const EXIT_ON_SDK_UPDATE = process.env.EXIT_ON_SDK_UPDATE !== 'false'; // Default: true - exit after SDK update so client reconnects with fresh SDK
|
|
83
84
|
// Get current installed version from package.json
|
|
84
85
|
async function getInstalledVersion(packageName) {
|
|
86
|
+
// Get package root (parent of dist/ where package.json and node_modules live)
|
|
87
|
+
const scriptDir = path.dirname(new URL(import.meta.url).pathname);
|
|
88
|
+
const packageRoot = path.resolve(scriptDir, '..');
|
|
89
|
+
const packageJsonPath = path.join(packageRoot, 'node_modules', ...packageName.split('/'), 'package.json');
|
|
85
90
|
try {
|
|
86
|
-
// For @morphllm/morphsdk, check the node_modules
|
|
87
|
-
const packageJsonPath = path.join(path.dirname(new URL(import.meta.url).pathname), '..', 'node_modules', ...packageName.split('/'), 'package.json');
|
|
88
91
|
const content = await fs.readFile(packageJsonPath, 'utf-8');
|
|
89
92
|
const pkg = JSON.parse(content);
|
|
90
93
|
return pkg.version || null;
|
|
91
94
|
}
|
|
92
95
|
catch {
|
|
93
|
-
//
|
|
94
|
-
|
|
95
|
-
const altPath = path.join(path.dirname(new URL(import.meta.url).pathname), 'node_modules', ...packageName.split('/'), 'package.json');
|
|
96
|
-
const content = await fs.readFile(altPath, 'utf-8');
|
|
97
|
-
const pkg = JSON.parse(content);
|
|
98
|
-
return pkg.version || null;
|
|
99
|
-
}
|
|
100
|
-
catch {
|
|
101
|
-
return null;
|
|
102
|
-
}
|
|
96
|
+
// Package not installed locally - this is fine, we'll fetch latest
|
|
97
|
+
return null;
|
|
103
98
|
}
|
|
104
99
|
}
|
|
105
100
|
// Fetch latest version from npm registry
|
|
@@ -132,6 +127,56 @@ async function checkPackageUpdate(packageName) {
|
|
|
132
127
|
updateAvailable,
|
|
133
128
|
};
|
|
134
129
|
}
|
|
130
|
+
// Clear npm cache for morph packages specifically
|
|
131
|
+
async function clearMorphCache() {
|
|
132
|
+
try {
|
|
133
|
+
const homeDir = os.homedir();
|
|
134
|
+
const npxCacheDir = path.join(homeDir, '.npm', '_npx');
|
|
135
|
+
// Find and remove morph-related npx cache entries
|
|
136
|
+
try {
|
|
137
|
+
const entries = await fs.readdir(npxCacheDir);
|
|
138
|
+
for (const entry of entries) {
|
|
139
|
+
const entryPath = path.join(npxCacheDir, entry, 'node_modules', '@morphllm');
|
|
140
|
+
try {
|
|
141
|
+
await fs.access(entryPath);
|
|
142
|
+
// Found a morph cache entry - remove the entire npx cache folder
|
|
143
|
+
const cacheFolder = path.join(npxCacheDir, entry);
|
|
144
|
+
await fs.rm(cacheFolder, { recursive: true, force: true });
|
|
145
|
+
console.error(`🗑️ Cleared npx cache: ${entry}`);
|
|
146
|
+
}
|
|
147
|
+
catch {
|
|
148
|
+
// Not a morph cache entry, skip
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
catch (error) {
|
|
153
|
+
// npx cache dir doesn't exist or can't be read
|
|
154
|
+
if (error.code !== 'ENOENT') {
|
|
155
|
+
reportMorphError({
|
|
156
|
+
error_message: `Failed to read npx cache dir: ${error instanceof Error ? error.message : String(error)}`,
|
|
157
|
+
error_type: 'CacheClearError',
|
|
158
|
+
context: {
|
|
159
|
+
action: 'read_npx_cache_dir',
|
|
160
|
+
npx_cache_dir: npxCacheDir
|
|
161
|
+
},
|
|
162
|
+
source: 'mcp-filesystem-autoupdate'
|
|
163
|
+
}).catch(() => { });
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
catch (error) {
|
|
168
|
+
// Report cache clear failures
|
|
169
|
+
reportMorphError({
|
|
170
|
+
error_message: `Cache clear failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
171
|
+
error_type: 'CacheClearError',
|
|
172
|
+
context: {
|
|
173
|
+
action: 'clear_morph_cache'
|
|
174
|
+
},
|
|
175
|
+
stack_trace: error instanceof Error ? error.stack : undefined,
|
|
176
|
+
source: 'mcp-filesystem-autoupdate'
|
|
177
|
+
}).catch(() => { });
|
|
178
|
+
}
|
|
179
|
+
}
|
|
135
180
|
// Run npm/bun update and wait for completion
|
|
136
181
|
function runPackageUpdate(packageName) {
|
|
137
182
|
return new Promise((resolve) => {
|
|
@@ -140,9 +185,26 @@ function runPackageUpdate(packageName) {
|
|
|
140
185
|
const args = packageManager === 'bun'
|
|
141
186
|
? ['add', packageName + '@latest']
|
|
142
187
|
: ['install', packageName + '@latest'];
|
|
143
|
-
|
|
188
|
+
// Get package root (parent of dist/ where package.json lives)
|
|
189
|
+
const scriptDir = path.dirname(new URL(import.meta.url).pathname);
|
|
190
|
+
const packageRoot = path.resolve(scriptDir, '..');
|
|
191
|
+
console.error(`🔧 Running: ${packageManager} ${args.join(' ')} in ${packageRoot}`);
|
|
192
|
+
// Check if we can write to the package root
|
|
193
|
+
fs.access(packageRoot, 2 /* W_OK */).catch(() => {
|
|
194
|
+
console.error(`⚠️ Warning: Package root may not be writable: ${packageRoot}`);
|
|
195
|
+
reportMorphError({
|
|
196
|
+
error_message: `Package root not writable: ${packageRoot}`,
|
|
197
|
+
error_type: 'AutoUpdatePermissionError',
|
|
198
|
+
context: {
|
|
199
|
+
package_name: packageName,
|
|
200
|
+
package_root: packageRoot,
|
|
201
|
+
action: 'check_write_permission'
|
|
202
|
+
},
|
|
203
|
+
source: 'mcp-filesystem-autoupdate'
|
|
204
|
+
}).catch(() => { });
|
|
205
|
+
});
|
|
144
206
|
const child = spawn(packageManager, args, {
|
|
145
|
-
cwd:
|
|
207
|
+
cwd: packageRoot,
|
|
146
208
|
stdio: 'pipe', // Capture output for debugging
|
|
147
209
|
env: { ...process.env, npm_config_fund: 'false', npm_config_audit: 'false' }, // Skip fund/audit for speed
|
|
148
210
|
});
|
|
@@ -150,16 +212,59 @@ function runPackageUpdate(packageName) {
|
|
|
150
212
|
child.stderr?.on('data', (data) => { stderr += data.toString(); });
|
|
151
213
|
child.on('error', (err) => {
|
|
152
214
|
console.error(`Update error: ${err.message}`);
|
|
215
|
+
// Report spawn errors (e.g., npm not found)
|
|
216
|
+
reportMorphError({
|
|
217
|
+
error_message: `Failed to spawn ${packageManager}: ${err.message}`,
|
|
218
|
+
error_type: 'AutoUpdateSpawnError',
|
|
219
|
+
context: {
|
|
220
|
+
package_name: packageName,
|
|
221
|
+
package_manager: packageManager,
|
|
222
|
+
package_root: packageRoot,
|
|
223
|
+
error_code: err.code,
|
|
224
|
+
action: 'spawn_package_manager',
|
|
225
|
+
// Include PATH for debugging
|
|
226
|
+
path_env: process.env.PATH?.split(':').slice(0, 5).join(':') + '...'
|
|
227
|
+
},
|
|
228
|
+
stack_trace: err.stack,
|
|
229
|
+
source: 'mcp-filesystem-autoupdate'
|
|
230
|
+
}).catch(() => { });
|
|
153
231
|
resolve(false);
|
|
154
232
|
});
|
|
155
233
|
child.on('close', (code) => {
|
|
156
|
-
if (code !== 0
|
|
157
|
-
console.error(`Update failed: ${stderr.slice(0,
|
|
234
|
+
if (code !== 0) {
|
|
235
|
+
console.error(`Update failed (exit ${code}): ${stderr.slice(0, 300)}`);
|
|
236
|
+
// Report non-zero exit
|
|
237
|
+
reportMorphError({
|
|
238
|
+
error_message: `${packageManager} exited with code ${code}`,
|
|
239
|
+
error_type: 'AutoUpdateExitError',
|
|
240
|
+
context: {
|
|
241
|
+
package_name: packageName,
|
|
242
|
+
package_manager: packageManager,
|
|
243
|
+
package_root: packageRoot,
|
|
244
|
+
exit_code: code,
|
|
245
|
+
stderr: stderr.slice(0, 1000),
|
|
246
|
+
action: 'npm_install'
|
|
247
|
+
},
|
|
248
|
+
source: 'mcp-filesystem-autoupdate'
|
|
249
|
+
}).catch(() => { });
|
|
158
250
|
}
|
|
159
251
|
resolve(code === 0);
|
|
160
252
|
});
|
|
161
253
|
// Timeout after 60 seconds
|
|
162
254
|
setTimeout(() => {
|
|
255
|
+
console.error(`Update timed out after 60s`);
|
|
256
|
+
reportMorphError({
|
|
257
|
+
error_message: `${packageManager} install timed out after 60s`,
|
|
258
|
+
error_type: 'AutoUpdateTimeoutError',
|
|
259
|
+
context: {
|
|
260
|
+
package_name: packageName,
|
|
261
|
+
package_manager: packageManager,
|
|
262
|
+
package_root: packageRoot,
|
|
263
|
+
stderr: stderr.slice(0, 1000),
|
|
264
|
+
action: 'npm_install_timeout'
|
|
265
|
+
},
|
|
266
|
+
source: 'mcp-filesystem-autoupdate'
|
|
267
|
+
}).catch(() => { });
|
|
163
268
|
child.kill();
|
|
164
269
|
resolve(false);
|
|
165
270
|
}, 60000);
|
|
@@ -167,9 +272,9 @@ function runPackageUpdate(packageName) {
|
|
|
167
272
|
}
|
|
168
273
|
// Main auto-update check function
|
|
169
274
|
async function checkAndUpdatePackages() {
|
|
275
|
+
// Only check SDK - MCP is updated via npx @morphllm/morphmcp@latest
|
|
170
276
|
const packagesToCheck = [
|
|
171
277
|
'@morphllm/morphsdk',
|
|
172
|
-
'@morphllm/morphmcp',
|
|
173
278
|
];
|
|
174
279
|
for (const packageName of packagesToCheck) {
|
|
175
280
|
try {
|
|
@@ -224,17 +329,51 @@ async function checkUpdateThenReportError(errorDetails) {
|
|
|
224
329
|
}
|
|
225
330
|
// Start auto-update interval
|
|
226
331
|
let autoUpdateInterval = null;
|
|
332
|
+
let sdkWasUpdatedAtStartup = false;
|
|
333
|
+
// Blocking startup check - runs before server starts
|
|
334
|
+
async function checkSdkAtStartup() {
|
|
335
|
+
if (DISABLE_AUTO_UPDATE) {
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
try {
|
|
339
|
+
const info = await checkPackageUpdate('@morphllm/morphsdk');
|
|
340
|
+
if (info.updateAvailable) {
|
|
341
|
+
console.error(`⚠️ SDK outdated: ${info.currentVersion} → ${info.latestVersion}`);
|
|
342
|
+
console.error(`📦 Updating SDK...`);
|
|
343
|
+
// Clear morph-related npm/npx cache first
|
|
344
|
+
await clearMorphCache();
|
|
345
|
+
const updateSucceeded = await runPackageUpdate('@morphllm/morphsdk');
|
|
346
|
+
if (updateSucceeded) {
|
|
347
|
+
console.error(`✅ SDK updated to ${info.latestVersion}`);
|
|
348
|
+
sdkWasUpdatedAtStartup = true;
|
|
349
|
+
if (EXIT_ON_SDK_UPDATE) {
|
|
350
|
+
console.error(`🔄 Restarting to use updated SDK...`);
|
|
351
|
+
// Exit with code 0 so MCP client will reconnect automatically
|
|
352
|
+
process.exit(0);
|
|
353
|
+
}
|
|
354
|
+
else {
|
|
355
|
+
console.error(`⚠️ RESTART REQUIRED: The updated SDK will be used on next restart.`);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
else {
|
|
359
|
+
console.error(`❌ SDK update failed - continuing with ${info.currentVersion}`);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
else {
|
|
363
|
+
console.error(`SDK version: ${info.currentVersion} (up to date)`);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
catch (error) {
|
|
367
|
+
console.error(`SDK version check failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
227
370
|
function startAutoUpdate() {
|
|
228
371
|
if (DISABLE_AUTO_UPDATE) {
|
|
229
372
|
console.error('Auto-update disabled via DISABLE_AUTO_UPDATE=true');
|
|
230
373
|
return;
|
|
231
374
|
}
|
|
232
375
|
console.error(`Auto-update enabled: checking every ${Math.round(AUTO_UPDATE_INTERVAL_MS / 60000)} minutes`);
|
|
233
|
-
//
|
|
234
|
-
setTimeout(() => {
|
|
235
|
-
checkAndUpdatePackages().catch(() => { });
|
|
236
|
-
}, 5000);
|
|
237
|
-
// Then check periodically
|
|
376
|
+
// Periodic checks (startup check is done separately before server starts)
|
|
238
377
|
autoUpdateInterval = setInterval(() => {
|
|
239
378
|
checkAndUpdatePackages().catch(() => { });
|
|
240
379
|
}, AUTO_UPDATE_INTERVAL_MS);
|
|
@@ -1241,13 +1380,15 @@ server.oninitialized = async () => {
|
|
|
1241
1380
|
};
|
|
1242
1381
|
// Start server
|
|
1243
1382
|
async function runServer() {
|
|
1383
|
+
// BLOCKING: Check and update SDK before starting server
|
|
1384
|
+
await checkSdkAtStartup();
|
|
1244
1385
|
const transport = new StdioServerTransport();
|
|
1245
1386
|
await server.connect(transport);
|
|
1246
1387
|
console.error("Secure MCP Filesystem Server running on stdio");
|
|
1247
1388
|
if (allowedDirectories.length === 0) {
|
|
1248
1389
|
console.error("Started without allowed directories - waiting for client to provide roots via MCP protocol");
|
|
1249
1390
|
}
|
|
1250
|
-
// Start auto-update system
|
|
1391
|
+
// Start periodic auto-update system
|
|
1251
1392
|
startAutoUpdate();
|
|
1252
1393
|
}
|
|
1253
1394
|
runServer().catch((error) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@morphllm/morphmcp",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.43",
|
|
4
4
|
"description": "Fast & accurate MCP server with AI-powered file editing and intelligent code search. Prevents context pollution and saves time for a better user experience.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Morph (https://morphllm.com)",
|
|
@@ -29,14 +29,14 @@
|
|
|
29
29
|
"build:obfuscated": "npm run build && npm run obfuscate",
|
|
30
30
|
"obfuscate": "npx javascript-obfuscator dist --output dist --target node --compact true --string-array true --exclude '**/dist/__tests__/**'",
|
|
31
31
|
"prepare": "npm run build",
|
|
32
|
-
"postinstall": "npm install @morphllm/morphsdk@latest --no-save
|
|
32
|
+
"postinstall": "echo '📦 Updating Morph SDK...' && npm install @morphllm/morphsdk@latest --no-save && echo '✅ SDK updated'",
|
|
33
33
|
"watch": "tsc --watch",
|
|
34
34
|
"test": "jest --config=jest.config.cjs --coverage"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
37
|
"@google/generative-ai": "^0.21.0",
|
|
38
38
|
"@modelcontextprotocol/sdk": "^1.12.3",
|
|
39
|
-
"@morphllm/morphsdk": "
|
|
39
|
+
"@morphllm/morphsdk": "^0.2.58",
|
|
40
40
|
"@vscode/ripgrep": "^1.15.14",
|
|
41
41
|
"axios": "^1.6.0",
|
|
42
42
|
"chalk": "^5.3.0",
|