@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.
- package/cli.js +97 -9
- package/package.json +1 -8
- 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('
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
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.
|
|
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
|
|
5
|
-
*
|
|
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
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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.');
|