deveco-mcp-server 0.1.7 → 0.1.9

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/index.js +341 -137
  2. package/package.json +5 -4
package/index.js CHANGED
@@ -1,179 +1,383 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- const { spawn } = require('child_process');
3
+ const { spawn, execSync } = require('child_process');
4
4
  const path = require('path');
5
5
  const fs = require('fs');
6
+ const os = require('os');
7
+
8
+ const OFFICIAL_REGISTRY = 'https://registry.npmjs.org';
9
+ const INSTALL_TIMEOUT = 60000;
10
+ const VERSION_CHECK_TIMEOUT = 5000; // 版本检查超时时间(5秒)
6
11
 
7
12
  function getPlatformPackage() {
8
13
  const platform = process.platform;
9
14
  const arch = process.arch;
10
15
 
11
- if (platform === 'win32' && arch === 'x64') {
12
- return 'deveco-mcp-server-win32-x64';
13
- } else if (platform === 'darwin' && arch === 'x64') {
14
- return 'deveco-mcp-server-darwin-x64';
15
- } else if (platform === 'darwin' && arch === 'arm64') {
16
- return 'deveco-mcp-server-darwin-arm64';
16
+ const platformMap = {
17
+ 'win32-x64': 'deveco-mcp-server-win32-x64',
18
+ 'darwin-x64': 'deveco-mcp-server-darwin-x64',
19
+ 'darwin-arm64': 'deveco-mcp-server-darwin-arm64'
20
+ };
21
+
22
+ const key = `${platform}-${arch}`;
23
+ if (platformMap[key]) {
24
+ return platformMap[key];
17
25
  }
18
26
 
19
27
  throw new Error(`Unsupported platform: ${platform}-${arch}`);
20
28
  }
21
29
 
30
+ function getNpmRegistry() {
31
+ try {
32
+ return execSync('npm config get registry', { encoding: 'utf-8' }).trim();
33
+ } catch (err) {
34
+ return null;
35
+ }
36
+ }
37
+
38
+ function checkAndWarnRegistry() {
39
+ const registry = getNpmRegistry();
40
+ if (registry && !registry.includes('npmjs.org')) {
41
+ console.warn(`\n⚠️ Detected non-official npm registry: ${registry}`);
42
+ console.warn(`Auto-install will use official registry (${OFFICIAL_REGISTRY}) to ensure package availability.\n`);
43
+ }
44
+ }
45
+
46
+ function buildBinaryPaths(binDir) {
47
+ if (process.platform === 'win32') {
48
+ return {
49
+ server: path.join(binDir, 'deveco-mcp-server.exe'),
50
+ toolbox: path.join(binDir, 'deveco-toolbox.exe')
51
+ };
52
+ } else {
53
+ return {
54
+ server: path.join(binDir, 'Contents', 'MacOS', 'deveco-mcp-server'),
55
+ toolbox: path.join(binDir, 'Contents', 'MacOS', 'deveco-toolbox')
56
+ };
57
+ }
58
+ }
59
+
60
+ function isNpxEnvironment() {
61
+ return __dirname.includes('_npx') ||
62
+ __dirname.includes('npm-cache') ||
63
+ __dirname.includes('npx-') ||
64
+ !!process.env.npm_config_user_config;
65
+ }
66
+
67
+ function getInstallDirectory() {
68
+ if (isNpxEnvironment()) {
69
+ const installDir = path.join(os.homedir(), '.deveco-mcp-cache');
70
+ if (!fs.existsSync(installDir)) {
71
+ fs.mkdirSync(installDir, { recursive: true });
72
+ }
73
+ return installDir;
74
+ }
75
+ return __dirname;
76
+ }
77
+
78
+ function findInstalledPackage(packageName, installDir) {
79
+ const nodeModulesPath = path.join(installDir, 'node_modules', packageName);
80
+ const packageJsonPath = path.join(nodeModulesPath, 'package.json');
81
+
82
+ if (fs.existsSync(packageJsonPath)) {
83
+ return nodeModulesPath;
84
+ }
85
+
86
+ // Try to resolve using require with multiple paths
87
+ const searchPaths = [
88
+ installDir,
89
+ path.join(installDir, 'node_modules'),
90
+ __dirname,
91
+ path.join(__dirname, 'node_modules')
92
+ ];
93
+
94
+ for (const searchPath of searchPaths) {
95
+ try {
96
+ const packagePath = require.resolve(packageName, { paths: [searchPath] });
97
+ return path.dirname(packagePath);
98
+ } catch (resolveErr) {
99
+ // Continue to next path
100
+ }
101
+ }
102
+
103
+ return nodeModulesPath;
104
+ }
105
+
106
+ function installPackage(packageName, installDir) {
107
+ try {
108
+ execSync(`npm install ${packageName}`, {
109
+ stdio: 'ignore',
110
+ cwd: installDir,
111
+ timeout: INSTALL_TIMEOUT
112
+ });
113
+ return findInstalledPackage(packageName, installDir);
114
+ } catch (installErr) {
115
+ // Try alternative: install to current directory with official registry
116
+ try {
117
+ execSync(`npm install --registry=${OFFICIAL_REGISTRY} ${packageName}`, {
118
+ stdio: 'ignore',
119
+ cwd: __dirname,
120
+ timeout: INSTALL_TIMEOUT
121
+ });
122
+
123
+ const localNodeModules = path.join(__dirname, 'node_modules', packageName);
124
+ const localPackageJson = path.join(localNodeModules, 'package.json');
125
+
126
+ if (fs.existsSync(localPackageJson)) {
127
+ return localNodeModules;
128
+ }
129
+ throw installErr;
130
+ } catch (altErr) {
131
+ throw new Error('All installation attempts failed');
132
+ }
133
+ }
134
+ }
135
+
136
+ function getLocalVersion(packageName, binDir) {
137
+ try {
138
+ const packageJsonPath = path.join(binDir, 'package.json');
139
+ if (fs.existsSync(packageJsonPath)) {
140
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
141
+ return packageJson.version || null;
142
+ }
143
+ } catch (err) {
144
+ // 忽略错误
145
+ }
146
+ return null;
147
+ }
148
+
149
+ function getLatestVersion(packageName) {
150
+ try {
151
+ const output = execSync(`npm view ${packageName} version --registry=${OFFICIAL_REGISTRY}`, {
152
+ encoding: 'utf-8',
153
+ timeout: VERSION_CHECK_TIMEOUT,
154
+ stdio: ['ignore', 'pipe', 'ignore']
155
+ });
156
+ return output.trim();
157
+ } catch (err) {
158
+ return null;
159
+ }
160
+ }
161
+
162
+ function compareVersions(version1, version2) {
163
+ // 简单的版本比较,如果需要更复杂的可以使用 semver 库
164
+ const v1parts = version1.split('.').map(Number);
165
+ const v2parts = version2.split('.').map(Number);
166
+
167
+ for (let i = 0; i < Math.max(v1parts.length, v2parts.length); i++) {
168
+ const v1part = v1parts[i] || 0;
169
+ const v2part = v2parts[i] || 0;
170
+ if (v1part < v2part) return -1;
171
+ if (v1part > v2part) return 1;
172
+ }
173
+ return 0;
174
+ }
175
+
176
+ function shouldSkipUpdate() {
177
+ // 默认自动更新,除非明确禁用
178
+ return process.env.DEVECO_MCP_NO_AUTO_UPDATE === 'true' ||
179
+ process.argv.includes('--no-update') ||
180
+ process.argv.includes('--skip-update');
181
+ }
182
+
183
+ function updatePackage(packageName, installDir) {
184
+ try {
185
+ execSync(`npm install ${packageName}@latest --registry=${OFFICIAL_REGISTRY}`, {
186
+ stdio: 'ignore',
187
+ cwd: installDir,
188
+ timeout: INSTALL_TIMEOUT
189
+ });
190
+ return findInstalledPackage(packageName, installDir);
191
+ } catch (err) {
192
+ // 如果更新失败,尝试在当前目录更新
193
+ try {
194
+ execSync(`npm install ${packageName}@latest --registry=${OFFICIAL_REGISTRY}`, {
195
+ stdio: 'ignore',
196
+ cwd: __dirname,
197
+ timeout: INSTALL_TIMEOUT
198
+ });
199
+ const localNodeModules = path.join(__dirname, 'node_modules', packageName);
200
+ if (fs.existsSync(path.join(localNodeModules, 'package.json'))) {
201
+ return localNodeModules;
202
+ }
203
+ } catch (altErr) {
204
+ // 更新失败,返回null
205
+ }
206
+ return null;
207
+ }
208
+ }
209
+
210
+ function checkAndUpdateIfNeeded(packageName, binDir) {
211
+ const localVersion = getLocalVersion(packageName, binDir);
212
+ if (!localVersion) {
213
+ return binDir; // 无法获取本地版本,直接返回
214
+ }
215
+
216
+ const latestVersion = getLatestVersion(packageName);
217
+ if (!latestVersion) {
218
+ return binDir; // 无法获取最新版本,直接返回
219
+ }
220
+
221
+ if (compareVersions(localVersion, latestVersion) < 0) {
222
+ // 本地版本较旧,默认自动更新
223
+ if (shouldSkipUpdate()) {
224
+ // 用户明确禁用了自动更新,仅提示
225
+ console.warn(`\n⚠️ New version available: ${latestVersion} (current: ${localVersion})`);
226
+ console.warn(` To enable auto-update, remove --no-update flag or DEVECO_MCP_NO_AUTO_UPDATE env var`);
227
+ console.warn(` Or manually run: npm install -g ${packageName}@latest\n`);
228
+ return binDir;
229
+ }
230
+
231
+ // 默认自动更新
232
+ const installDir = getInstallDirectory();
233
+ const updatedBinDir = updatePackage(packageName, installDir);
234
+ if (updatedBinDir) {
235
+ return updatedBinDir;
236
+ } else {
237
+ console.error(`⚠️ Failed to update ${packageName}, using existing version ${localVersion}`);
238
+ return binDir;
239
+ }
240
+ }
241
+
242
+ return binDir;
243
+ }
244
+
245
+ function printTroubleshootingSteps(packageName) {
246
+ console.error('\n❌ Failed to install platform package automatically.');
247
+ console.error('\nTroubleshooting steps:');
248
+ console.error(`\n1. Check npm registry (should be ${OFFICIAL_REGISTRY}/):`);
249
+ console.error(` npm config get registry`);
250
+ console.error(` npm config set registry ${OFFICIAL_REGISTRY}/`);
251
+ console.error(`\n2. Clear npm cache:`);
252
+ console.error(` npm cache clean --force`);
253
+ console.error(`\n3. Manual install (recommended):`);
254
+ console.error(` npm install -g ${packageName}`);
255
+ console.error(` npm install -g deveco-mcp-server`);
256
+ console.error(`\n4. Or use with registry flag in MCP config:`);
257
+ console.error(` "args": ["--registry=${OFFICIAL_REGISTRY}", "-y", "deveco-mcp-server"]`);
258
+ console.error(`\n5. Check if package exists:`);
259
+ console.error(` npm view ${packageName}`);
260
+ }
261
+
22
262
  function getBinaryPaths() {
23
263
  const packageName = getPlatformPackage();
24
264
 
265
+ // Try to resolve existing package
25
266
  try {
26
267
  const packagePath = require.resolve(packageName);
27
- const binDir = path.dirname(packagePath);
268
+ let binDir = path.dirname(packagePath);
28
269
 
29
- if (process.platform === 'win32') {
30
- return {
31
- server: path.join(binDir, 'deveco-mcp-server.exe'),
32
- toolbox: path.join(binDir, 'deveco-toolbox.exe')
33
- };
34
- } else {
35
- return {
36
- server: path.join(binDir, 'Contents', 'MacOS', 'deveco-mcp-server'),
37
- toolbox: path.join(binDir, 'Contents', 'MacOS', 'deveco-toolbox')
38
- };
39
- }
40
- } catch (err) {
41
- console.error(`Platform package ${packageName} not found.`);
42
- console.error('Installing platform package automatically...');
270
+ // Check for updates if package is found locally
271
+ binDir = checkAndUpdateIfNeeded(packageName, binDir);
43
272
 
44
- const { execSync } = require('child_process');
45
- const os = require('os');
273
+ const binaryPaths = buildBinaryPaths(binDir);
46
274
 
47
- // Detect if we're in npx temporary directory
48
- const isNpx = __dirname.includes('_npx') ||
49
- __dirname.includes('npm-cache') ||
50
- __dirname.includes('npx-') ||
51
- process.env.npm_config_user_config;
275
+ // Verify server binary exists
276
+ if (!fs.existsSync(binaryPaths.server)) {
277
+ console.error(`Error: Server executable not found at ${binaryPaths.server}`);
278
+ console.error('Please run: npm install --force');
279
+ process.exit(1);
280
+ }
52
281
 
53
- let installDir = __dirname;
54
- let binDir = null;
282
+ return binaryPaths;
283
+ } catch (err) {
55
284
 
56
- // If in npx, install to user's home directory cache
57
- if (isNpx) {
58
- const homeDir = os.homedir();
59
- installDir = path.join(homeDir, '.deveco-mcp-cache');
60
-
61
- // Create cache directory if it doesn't exist
62
- if (!fs.existsSync(installDir)) {
63
- fs.mkdirSync(installDir, { recursive: true });
64
- }
65
- }
285
+ checkAndWarnRegistry();
286
+
287
+ const installDir = getInstallDirectory();
288
+ let binDir;
66
289
 
67
290
  try {
68
- // Install the platform package
69
- execSync(`npm install ${packageName}`, {
70
- stdio: 'pipe',
71
- cwd: installDir,
72
- timeout: 60000 // 60 second timeout
73
- });
74
-
75
- // Find the installed package
76
- const nodeModulesPath = path.join(installDir, 'node_modules', packageName);
77
- if (fs.existsSync(nodeModulesPath)) {
78
- binDir = nodeModulesPath;
79
- } else {
80
- // Try to resolve using require
81
- try {
82
- const packagePath = require.resolve(packageName, { paths: [installDir] });
83
- binDir = path.dirname(packagePath);
84
- } catch (resolveErr) {
85
- // If resolve fails, use the node_modules path directly
86
- binDir = nodeModulesPath;
87
- }
88
- }
291
+ binDir = installPackage(packageName, installDir);
89
292
  } catch (installErr) {
90
- // Installation failed, try alternative: install to current directory
91
- try {
92
- execSync(`npm install ${packageName}`, {
93
- stdio: 'pipe',
94
- cwd: __dirname,
95
- timeout: 60000
96
- });
97
-
98
- const localNodeModules = path.join(__dirname, 'node_modules', packageName);
99
- if (fs.existsSync(localNodeModules)) {
100
- binDir = localNodeModules;
101
- } else {
102
- throw installErr;
103
- }
104
- } catch (altErr) {
105
- // All installation attempts failed
106
- console.error('\n❌ Failed to install platform package automatically.');
107
- console.error('\nPlease try one of the following:');
108
- console.error(`\n1. Global install (recommended):`);
109
- console.error(` npm install -g deveco-mcp-server`);
110
- console.error(`\n2. Manual install:`);
111
- console.error(` npm install -g ${packageName}`);
112
- console.error(` npm install -g deveco-mcp-server`);
113
- console.error(`\n3. Force reinstall:`);
114
- console.error(` npm cache clean --force`);
115
- console.error(` npm install -g deveco-mcp-server`);
116
- process.exit(1);
117
- }
293
+ printTroubleshootingSteps(packageName);
294
+ process.exit(1);
118
295
  }
119
296
 
120
- // Use the installed package path
121
- if (binDir && fs.existsSync(binDir)) {
122
- if (process.platform === 'win32') {
123
- return {
124
- server: path.join(binDir, 'deveco-mcp-server.exe'),
125
- toolbox: path.join(binDir, 'deveco-toolbox.exe')
126
- };
127
- } else {
128
- return {
129
- server: path.join(binDir, 'Contents', 'MacOS', 'deveco-mcp-server'),
130
- toolbox: path.join(binDir, 'Contents', 'MacOS', 'deveco-toolbox')
131
- };
132
- }
297
+ // Verify the installed package exists and has the expected structure
298
+ if (!binDir || !fs.existsSync(binDir)) {
299
+ console.error('Failed to locate installed platform package.');
300
+ process.exit(1);
133
301
  }
134
302
 
135
- // Should not reach here, but just in case
136
- console.error('Failed to locate installed platform package.');
137
- process.exit(1);
303
+ const binaryPaths = buildBinaryPaths(binDir);
304
+
305
+ // Verify server binary exists
306
+ if (!fs.existsSync(binaryPaths.server)) {
307
+ console.error(`Installed package found at ${binDir}, but binary not at expected location.`);
308
+ console.error(`Expected: ${binaryPaths.server}`);
309
+ process.exit(1);
310
+ }
311
+
312
+ return binaryPaths;
138
313
  }
139
314
  }
140
315
 
141
- const { server: serverExePath, toolbox: toolboxExePath } = getBinaryPaths();
142
-
143
- if (!fs.existsSync(serverExePath)) {
144
- console.error(`Error: Server executable not found at ${serverExePath}`);
145
- console.error('Please run: npm install --force');
146
- process.exit(1);
147
- }
148
-
149
- // Ensure execution permissions on Unix-like systems
150
- if (process.platform !== 'win32') {
316
+ function ensureExecutablePermissions(filePath) {
317
+ if (process.platform === 'win32') {
318
+ return;
319
+ }
320
+
151
321
  try {
152
- fs.chmodSync(serverExePath, 0o755);
153
- if (toolboxExePath && fs.existsSync(toolboxExePath)) {
154
- fs.chmodSync(toolboxExePath, 0o755);
155
- }
322
+ fs.chmodSync(filePath, 0o755);
156
323
  } catch (err) {
157
- console.warn(`Warning: Failed to set executable permissions: ${err.message}`);
324
+ console.error(`Warning: Failed to set executable permissions for ${filePath}: ${err.message}`);
325
+ }
326
+
327
+ // On macOS, remove quarantine attribute and ad-hoc sign to avoid "Unknown system error -86"
328
+ if (process.platform === 'darwin') {
329
+ try {
330
+ // Remove quarantine attribute
331
+ execSync(`xattr -d com.apple.quarantine "${filePath}" 2>/dev/null || true`, {
332
+ stdio: 'ignore',
333
+ timeout: 5000
334
+ });
335
+
336
+ // Ad-hoc sign the binary
337
+ execSync(`codesign --force --sign - "${filePath}" 2>/dev/null`, {
338
+ stdio: 'ignore',
339
+ timeout: 10000
340
+ });
341
+ } catch (err) {
342
+ // Silently ignore - binary might already be signed or user lacks permissions
343
+ }
158
344
  }
159
345
  }
160
346
 
161
- const args = process.argv.slice(2);
162
- const env = {
163
- ...process.env,
164
- DEVECO_TOOLBOX_PATH: toolboxExePath
165
- };
347
+ function startServer() {
348
+ const { server: serverExePath, toolbox: toolboxExePath } = getBinaryPaths();
349
+
350
+ if (!fs.existsSync(serverExePath)) {
351
+ console.error(`Error: Server executable not found at ${serverExePath}`);
352
+ console.error('Please run: npm install --force');
353
+ process.exit(1);
354
+ }
355
+
356
+ // Ensure execution permissions on Unix-like systems
357
+ ensureExecutablePermissions(serverExePath);
358
+ if (toolboxExePath && fs.existsSync(toolboxExePath)) {
359
+ ensureExecutablePermissions(toolboxExePath);
360
+ }
361
+
362
+ const args = process.argv.slice(2);
363
+ const env = {
364
+ ...process.env,
365
+ DEVECO_TOOLBOX_PATH: toolboxExePath
366
+ };
166
367
 
167
- const child = spawn(serverExePath, args, {
168
- stdio: 'inherit',
169
- env: env
170
- });
368
+ const child = spawn(serverExePath, args, {
369
+ stdio: 'inherit',
370
+ env: env
371
+ });
171
372
 
172
- child.on('error', (err) => {
173
- console.error('Failed to start deveco-mcp-server:', err);
174
- process.exit(1);
175
- });
373
+ child.on('error', (err) => {
374
+ console.error('Failed to start deveco-mcp-server:', err);
375
+ process.exit(1);
376
+ });
377
+
378
+ child.on('close', (code) => {
379
+ process.exit(code);
380
+ });
381
+ }
176
382
 
177
- child.on('close', (code) => {
178
- process.exit(code);
179
- });
383
+ startServer();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "deveco-mcp-server",
3
- "version": "0.1.7",
3
+ "version": "0.1.9",
4
4
  "description": "NPM wrapper for deveco-mcp-server - HarmonyOS development MCP server with platform-specific binaries",
5
5
  "keywords": [
6
6
  "mcp",
@@ -16,12 +16,13 @@
16
16
  "deveco-mcp-server": "index.js"
17
17
  },
18
18
  "optionalDependencies": {
19
- "deveco-mcp-server-win32-x64": "0.1.7",
20
- "deveco-mcp-server-darwin-arm64": "0.1.7"
19
+ "deveco-mcp-server-darwin-arm64": "0.1.9",
20
+ "deveco-mcp-server-darwin-x64": "0.1.9",
21
+ "deveco-mcp-server-win32-x64": "^0.1.9"
21
22
  },
22
23
  "repository": {
23
24
  "type": "git",
24
- "url": "https://github.com/your-org/deveco-mcp.git"
25
+ "url": "https://github.com/open-deveco/deveco-toolbox.git"
25
26
  },
26
27
  "author": "",
27
28
  "license": "ISC"