@khanglvm/tool-hub-mcp 1.1.4 → 1.1.6

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 (3) hide show
  1. package/cli.js +97 -9
  2. package/package.json +1 -8
  3. package/postinstall.js +5 -241
package/cli.js CHANGED
@@ -91,22 +91,110 @@ function findDownloadedBinary() {
91
91
  return null;
92
92
  }
93
93
 
94
+ /**
95
+ * Download binary from GitHub releases.
96
+ * @returns {Promise<string>} Path to downloaded binary
97
+ */
98
+ async function downloadBinary() {
99
+ const https = require('https');
100
+ const fs = require('fs');
101
+ const path = require('path');
102
+ const { execSync } = require('child_process');
103
+ const os = require('os');
104
+
105
+ const platform = os.platform();
106
+ const arch = os.arch();
107
+ const platformMap = { 'darwin': 'Darwin', 'linux': 'Linux', 'win32': 'Windows' };
108
+ const archMap = { 'arm64': 'arm64', 'x64': 'x86_64' };
109
+ const suffix = `${platformMap[platform]}-${archMap[arch]}`;
110
+ const binaryName = platform === 'win32' ? 'tool-hub-mcp.exe' : 'tool-hub-mcp';
111
+
112
+ // Get version from package.json
113
+ const packageJson = require('./package.json');
114
+ const version = packageJson.version;
115
+
116
+ const downloadUrl = `https://github.com/khanglvm/tool-hub-mcp/releases/download/v${version}/tool-hub-mcp-${suffix}`;
117
+ const binDir = path.join(__dirname, 'bin');
118
+ const destPath = path.join(binDir, binaryName);
119
+
120
+ console.error(`tool-hub-mcp: Downloading binary from ${downloadUrl}...`);
121
+
122
+ if (!fs.existsSync(binDir)) {
123
+ fs.mkdirSync(binDir, { recursive: true });
124
+ }
125
+
126
+ // Use curl if available, otherwise https.get
127
+ try {
128
+ execSync(`curl -fsSL ${downloadUrl} -o ${destPath}`, { stdio: 'ignore' });
129
+ if (fs.existsSync(destPath)) {
130
+ if (platform !== 'win32') {
131
+ fs.chmodSync(destPath, 0o755);
132
+ }
133
+ console.error(`tool-hub-mcp: Binary downloaded successfully`);
134
+ return destPath;
135
+ }
136
+ } catch (e) {
137
+ // curl failed or binary not downloaded, fall through to https.get
138
+ }
139
+
140
+ // Fallback to https.get
141
+ return new Promise((resolve, reject) => {
142
+ const file = fs.createWriteStream(destPath);
143
+ https.get(downloadUrl, (response) => {
144
+ if (response.statusCode === 301 || response.statusCode === 302) {
145
+ file.close();
146
+ https.get(response.headers.location, (resp2) => {
147
+ if (resp2.statusCode === 200) {
148
+ resp2.pipe(file);
149
+ file.on('finish', () => {
150
+ file.close();
151
+ if (platform !== 'win32') {
152
+ fs.chmodSync(destPath, 0o755);
153
+ }
154
+ console.error(`tool-hub-mcp: Binary downloaded successfully`);
155
+ resolve(destPath);
156
+ });
157
+ } else {
158
+ reject(new Error(`Failed to download: HTTP ${resp2.statusCode}`));
159
+ }
160
+ });
161
+ return;
162
+ }
163
+ if (response.statusCode !== 200) {
164
+ reject(new Error(`HTTP ${response.statusCode}`));
165
+ return;
166
+ }
167
+ response.pipe(file);
168
+ file.on('finish', () => {
169
+ file.close();
170
+ if (platform !== 'win32') {
171
+ fs.chmodSync(destPath, 0o755);
172
+ }
173
+ console.error(`tool-hub-mcp: Binary downloaded successfully`);
174
+ resolve(destPath);
175
+ });
176
+ }).on('error', reject);
177
+ });
178
+ }
179
+
94
180
  /**
95
181
  * Main entry point - find binary and spawn with all arguments.
96
182
  */
97
- function main() {
183
+ async function main() {
98
184
  // Try platform package first, then fallback to downloaded binary
99
185
  let binaryPath = findBinaryFromPackage() || findDownloadedBinary();
100
186
 
101
187
  if (!binaryPath) {
102
- console.error('Error: tool-hub-mcp binary not found.');
103
- console.error('This may happen if:');
104
- console.error(' 1. optionalDependencies were disabled during install');
105
- console.error(' 2. postinstall script failed to download the binary');
106
- console.error('');
107
- console.error('Try reinstalling: npm install @khanglvm/tool-hub-mcp');
108
- console.error('Or download manually from: https://github.com/khanglvm/tool-hub-mcp/releases');
109
- process.exit(1);
188
+ console.error('tool-hub-mcp: Binary not found, downloading from GitHub...');
189
+ try {
190
+ binaryPath = await downloadBinary();
191
+ } catch (err) {
192
+ console.error('Error: Failed to download binary.');
193
+ console.error(` ${err.message}`);
194
+ console.error('');
195
+ console.error('You can manually download from: https://github.com/khanglvm/tool-hub-mcp/releases');
196
+ process.exit(1);
197
+ }
110
198
  }
111
199
 
112
200
  // Spawn the binary with all arguments passed through
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@khanglvm/tool-hub-mcp",
3
- "version": "1.1.4",
3
+ "version": "1.1.6",
4
4
  "description": "Serverless MCP Aggregator - Reduce AI context token consumption by 38%",
5
5
  "bin": {
6
6
  "tool-hub-mcp": "./cli.js"
@@ -13,13 +13,6 @@
13
13
  "scripts": {
14
14
  "postinstall": "node postinstall.js"
15
15
  },
16
- "optionalDependencies": {
17
- "@khanglvm/tool-hub-mcp-darwin-arm64": "1.1.4",
18
- "@khanglvm/tool-hub-mcp-darwin-x64": "1.1.4",
19
- "@khanglvm/tool-hub-mcp-linux-x64": "1.1.4",
20
- "@khanglvm/tool-hub-mcp-linux-arm64": "1.1.4",
21
- "@khanglvm/tool-hub-mcp-win32-x64": "1.1.4"
22
- },
23
16
  "keywords": [
24
17
  "mcp",
25
18
  "model-context-protocol",
package/postinstall.js CHANGED
@@ -1,246 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  /**
4
- * Postinstall fallback for tool-hub-mcp.
5
- * Downloads the Go binary from GitHub Releases if optionalDependencies were not installed.
6
- *
7
- * This handles environments where:
8
- * - optionalDependencies are disabled (some CI environments)
9
- * - Package manager fails to resolve platform-specific packages
4
+ * Postinstall script for tool-hub-mcp.
5
+ * The binary is downloaded on-demand by cli.js when first executed.
10
6
  */
11
7
 
12
- const https = require('https');
13
- const fs = require('fs');
14
- const path = require('path');
15
- const os = require('os');
16
- const { execSync } = require('child_process');
17
-
18
- // Configuration
19
- const GITHUB_REPO = 'khanglvm/tool-hub-mcp';
20
- const BINARY_NAME = 'tool-hub-mcp';
21
-
22
- /**
23
- * Get platform suffix for GitHub release assets.
24
- * @returns {string} Suffix like 'Darwin-arm64' or 'Linux-x86_64'
25
- */
26
- function getPlatformSuffix() {
27
- const platform = os.platform();
28
- const arch = os.arch();
29
-
30
- const platformMap = {
31
- 'darwin': 'Darwin',
32
- 'linux': 'Linux',
33
- 'win32': 'Windows'
34
- };
35
-
36
- const archMap = {
37
- 'arm64': 'arm64',
38
- 'x64': 'x86_64'
39
- };
40
-
41
- const mappedPlatform = platformMap[platform];
42
- const mappedArch = archMap[arch];
43
-
44
- if (!mappedPlatform || !mappedArch) {
45
- throw new Error(`Unsupported platform: ${platform}-${arch}`);
46
- }
47
-
48
- const suffix = `${mappedPlatform}-${mappedArch}`;
49
- return platform === 'win32' ? `${suffix}.exe` : suffix;
50
- }
51
-
52
- /**
53
- * Check if platform package binary already exists.
54
- * @returns {boolean} True if binary found in optionalDependencies
55
- */
56
- function hasPlatformBinary() {
57
- const platform = os.platform();
58
- const arch = os.arch();
59
-
60
- const archMap = { 'arm64': 'arm64', 'x64': 'x64' };
61
- const platformMap = { 'darwin': 'darwin', 'linux': 'linux', 'win32': 'win32' };
62
-
63
- const packageName = `@khanglvm/tool-hub-mcp-${platformMap[platform]}-${archMap[arch]}`;
64
- const binaryName = platform === 'win32' ? `${BINARY_NAME}.exe` : BINARY_NAME;
65
-
66
- const searchPaths = [
67
- path.join(__dirname, 'node_modules', packageName, 'bin', binaryName),
68
- path.join(__dirname, '..', packageName, 'bin', binaryName),
69
- path.join(__dirname, '..', '..', packageName, 'bin', binaryName),
70
- ];
71
-
72
- return searchPaths.some(p => fs.existsSync(p));
73
- }
74
-
75
- /**
76
- * Follow redirects and download file.
77
- * @param {string} url - URL to download
78
- * @param {string} dest - Destination file path
79
- * @returns {Promise<void>}
80
- */
81
- function downloadFile(url, dest) {
82
- return new Promise((resolve, reject) => {
83
- const file = fs.createWriteStream(dest);
84
-
85
- const request = (url) => {
86
- https.get(url, (response) => {
87
- // Handle redirects (GitHub releases use 302)
88
- if (response.statusCode === 301 || response.statusCode === 302) {
89
- file.close();
90
- fs.unlinkSync(dest);
91
- request(response.headers.location);
92
- return;
93
- }
94
-
95
- if (response.statusCode !== 200) {
96
- file.close();
97
- fs.unlinkSync(dest);
98
- reject(new Error(`HTTP ${response.statusCode}: Failed to download ${url}`));
99
- return;
100
- }
101
-
102
- response.pipe(file);
103
- file.on('finish', () => {
104
- file.close();
105
- resolve();
106
- });
107
- }).on('error', (err) => {
108
- file.close();
109
- fs.unlinkSync(dest);
110
- reject(err);
111
- });
112
- };
113
-
114
- request(url);
115
- });
116
- }
117
-
118
- /**
119
- * Get the latest release version from GitHub API.
120
- * @returns {Promise<string>} Version tag like 'v1.0.0'
121
- */
122
- function getLatestVersion() {
123
- return new Promise((resolve, reject) => {
124
- const options = {
125
- hostname: 'api.github.com',
126
- path: `/repos/${GITHUB_REPO}/releases/latest`,
127
- headers: { 'User-Agent': 'tool-hub-mcp-installer' }
128
- };
129
-
130
- https.get(options, (response) => {
131
- let data = '';
132
- response.on('data', chunk => data += chunk);
133
- response.on('end', () => {
134
- try {
135
- const release = JSON.parse(data);
136
- if (release.tag_name) {
137
- resolve(release.tag_name);
138
- } else {
139
- reject(new Error('No releases found'));
140
- }
141
- } catch (e) {
142
- reject(e);
143
- }
144
- });
145
- }).on('error', reject);
146
- });
147
- }
148
-
149
- /**
150
- * Main postinstall logic.
151
- */
152
- async function main() {
153
- // Skip if platform binary already installed via optionalDependencies
154
- if (hasPlatformBinary()) {
155
- console.log('tool-hub-mcp: Binary found from optionalDependencies ✓');
156
- createBinSymlink(BINARY_NAME);
157
- return;
158
- }
159
-
160
- console.log('tool-hub-mcp: optionalDependencies not available, downloading from GitHub...');
161
-
162
- try {
163
- // Get latest version
164
- const version = await getLatestVersion();
165
- console.log(`tool-hub-mcp: Downloading version ${version}...`);
166
-
167
- // Prepare download
168
- const suffix = getPlatformSuffix();
169
- const binaryName = os.platform() === 'win32' ? `${BINARY_NAME}.exe` : BINARY_NAME;
170
- const downloadUrl = `https://github.com/${GITHUB_REPO}/releases/download/${version}/${BINARY_NAME}-${suffix}`;
171
-
172
- // Create bin directory
173
- const binDir = path.join(__dirname, 'bin');
174
- if (!fs.existsSync(binDir)) {
175
- fs.mkdirSync(binDir, { recursive: true });
176
- }
177
-
178
- const destPath = path.join(binDir, binaryName);
179
-
180
- // Download binary
181
- await downloadFile(downloadUrl, destPath);
182
-
183
- // Make executable (Unix only)
184
- if (os.platform() !== 'win32') {
185
- fs.chmodSync(destPath, 0o755);
186
- }
187
-
188
- console.log('tool-hub-mcp: Binary downloaded and installed ✓');
189
- createBinSymlink(binaryName);
190
-
191
- } catch (error) {
192
- console.error(`tool-hub-mcp: Failed to download binary: ${error.message}`);
193
- console.error('You can manually download from: https://github.com/khanglvm/tool-hub-mcp/releases');
194
- // Don't fail the install - user can still manually download
195
- process.exit(0);
196
- }
197
- }
198
-
199
- /**
200
- * Create .bin symlink for the CLI wrapper.
201
- * This is needed because npm sometimes doesn't create symlinks properly
202
- * when packages are installed via npx or when optionalDependencies are used.
203
- */
204
- function createBinSymlink(binaryName) {
205
- try {
206
- const fs = require('fs');
207
- const path = require('path');
208
-
209
- // Find the .bin directory (usually at node_modules/.bin)
210
- let binDir = null;
211
- const searchLevels = ['node_modules/.bin', '../.bin', '../../.bin', '.bin'];
212
-
213
- for (const dir of searchLevels) {
214
- const testPath = path.join(__dirname, dir);
215
- // Check if path exists or if we can create it
216
- const parentDir = path.dirname(testPath);
217
- if (fs.existsSync(parentDir)) {
218
- // Create .bin directory if it doesn't exist
219
- if (!fs.existsSync(testPath)) {
220
- fs.mkdirSync(testPath, { recursive: true });
221
- }
222
- binDir = testPath;
223
- break;
224
- }
225
- }
226
-
227
- if (!binDir) {
228
- // .bin directory doesn't exist, might be in a global install
229
- return;
230
- }
231
-
232
- const linkPath = path.join(binDir, binaryName);
233
- const targetPath = path.join(__dirname, 'cli.js');
234
-
235
- // Create symlink if it doesn't exist
236
- if (!fs.existsSync(linkPath)) {
237
- fs.symlinkSync(targetPath, linkPath);
238
- console.log('tool-hub-mcp: Created .bin symlink ✓');
239
- }
240
- } catch (err) {
241
- // Log error but don't fail
242
- console.log('tool-hub-mcp: Could not create .bin symlink:', err.message);
243
- }
244
- }
245
-
246
- main();
8
+ // Log installation info
9
+ console.log('tool-hub-mcp v' + require('./package.json').version + ' installed!');
10
+ console.log('Binary will be downloaded automatically on first use.');