@sknoble/slvsx-mcp-server 0.1.1 → 0.1.2
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/mcp-server.js +14 -7
- package/package.json +4 -2
- package/scripts/postinstall.js +193 -0
package/mcp-server.js
CHANGED
|
@@ -23,14 +23,22 @@ import { fileURLToPath } from 'url';
|
|
|
23
23
|
const __filename = fileURLToPath(import.meta.url);
|
|
24
24
|
const __dirname = path.dirname(__filename);
|
|
25
25
|
|
|
26
|
-
// Find slvsx binary - check env var, PATH, then local build
|
|
26
|
+
// Find slvsx binary - check env var, bundled, PATH, then local build
|
|
27
27
|
function findSlvsxBinary() {
|
|
28
|
+
const ext = process.platform === 'win32' ? '.exe' : '';
|
|
29
|
+
|
|
28
30
|
// 1. Check explicit env var
|
|
29
31
|
if (process.env.SLVSX_BINARY) {
|
|
30
32
|
return process.env.SLVSX_BINARY;
|
|
31
33
|
}
|
|
32
34
|
|
|
33
|
-
// 2. Check
|
|
35
|
+
// 2. Check bundled binary (installed via postinstall)
|
|
36
|
+
const bundled = path.join(__dirname, 'bin', `slvsx${ext}`);
|
|
37
|
+
if (fs.existsSync(bundled)) {
|
|
38
|
+
return bundled;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// 3. Check if slvsx is in PATH
|
|
34
42
|
try {
|
|
35
43
|
const which = execSync('which slvsx 2>/dev/null || where slvsx 2>nul', { encoding: 'utf-8' }).trim();
|
|
36
44
|
if (which) return which.split('\n')[0];
|
|
@@ -38,15 +46,14 @@ function findSlvsxBinary() {
|
|
|
38
46
|
// Not in PATH
|
|
39
47
|
}
|
|
40
48
|
|
|
41
|
-
//
|
|
42
|
-
const localBuild =
|
|
49
|
+
// 4. Check local build
|
|
50
|
+
const localBuild = `./target/release/slvsx${ext}`;
|
|
43
51
|
if (fs.existsSync(localBuild)) {
|
|
44
52
|
return localBuild;
|
|
45
53
|
}
|
|
46
54
|
|
|
47
|
-
//
|
|
48
|
-
const
|
|
49
|
-
const devBuild = path.join(scriptDir, 'target/release/slvsx');
|
|
55
|
+
// 5. Check relative to this script (for development)
|
|
56
|
+
const devBuild = path.join(__dirname, 'target/release', `slvsx${ext}`);
|
|
50
57
|
if (fs.existsSync(devBuild)) {
|
|
51
58
|
return devBuild;
|
|
52
59
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sknoble/slvsx-mcp-server",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "MCP server for SLVSX geometric constraint solver",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -10,7 +10,8 @@
|
|
|
10
10
|
"scripts": {
|
|
11
11
|
"start": "node mcp-server.js",
|
|
12
12
|
"build:docs": "npx tsx scripts/build-docs.ts",
|
|
13
|
-
"test": "node tests/mcp-server.test.js"
|
|
13
|
+
"test": "node tests/mcp-server.test.js",
|
|
14
|
+
"postinstall": "node scripts/postinstall.js"
|
|
14
15
|
},
|
|
15
16
|
"dependencies": {
|
|
16
17
|
"@langchain/textsplitters": "^0.1.0",
|
|
@@ -38,6 +39,7 @@
|
|
|
38
39
|
"files": [
|
|
39
40
|
"mcp-server.js",
|
|
40
41
|
"dist/docs.json",
|
|
42
|
+
"scripts/postinstall.js",
|
|
41
43
|
"README.md"
|
|
42
44
|
],
|
|
43
45
|
"engines": {
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Postinstall script that downloads the slvsx binary for the current platform
|
|
5
|
+
* from GitHub releases.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import fs from 'fs';
|
|
9
|
+
import path from 'path';
|
|
10
|
+
import { fileURLToPath } from 'url';
|
|
11
|
+
import https from 'https';
|
|
12
|
+
import { createWriteStream } from 'fs';
|
|
13
|
+
import { exec } from 'child_process';
|
|
14
|
+
import { promisify } from 'util';
|
|
15
|
+
|
|
16
|
+
const execAsync = promisify(exec);
|
|
17
|
+
|
|
18
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
19
|
+
const __dirname = path.dirname(__filename);
|
|
20
|
+
const packageRoot = path.resolve(__dirname, '..');
|
|
21
|
+
|
|
22
|
+
// Map Node.js platform/arch to our release asset naming
|
|
23
|
+
function getPlatformInfo() {
|
|
24
|
+
const platform = process.platform;
|
|
25
|
+
const arch = process.arch;
|
|
26
|
+
|
|
27
|
+
// Map to actual release asset names like: slvsx-macos-arm64.tar.gz, slvsx-linux.tar.gz
|
|
28
|
+
if (platform === 'darwin') {
|
|
29
|
+
if (arch === 'arm64') {
|
|
30
|
+
return { assetPattern: 'macos-arm64', extension: '' };
|
|
31
|
+
} else if (arch === 'x64') {
|
|
32
|
+
return { assetPattern: 'macos-x86_64', extension: '' };
|
|
33
|
+
}
|
|
34
|
+
} else if (platform === 'linux') {
|
|
35
|
+
// Linux release is just "linux" (x86_64)
|
|
36
|
+
if (arch === 'x64') {
|
|
37
|
+
return { assetPattern: 'linux', extension: '' };
|
|
38
|
+
}
|
|
39
|
+
} else if (platform === 'win32') {
|
|
40
|
+
if (arch === 'x64') {
|
|
41
|
+
return { assetPattern: 'windows', extension: '.exe' };
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async function getLatestRelease() {
|
|
49
|
+
return new Promise((resolve, reject) => {
|
|
50
|
+
const options = {
|
|
51
|
+
hostname: 'api.github.com',
|
|
52
|
+
path: '/repos/snoble/slvsx-cli/releases/latest',
|
|
53
|
+
headers: {
|
|
54
|
+
'User-Agent': 'slvsx-mcp-server-installer'
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
https.get(options, (res) => {
|
|
59
|
+
let data = '';
|
|
60
|
+
res.on('data', chunk => data += chunk);
|
|
61
|
+
res.on('end', () => {
|
|
62
|
+
try {
|
|
63
|
+
resolve(JSON.parse(data));
|
|
64
|
+
} catch (e) {
|
|
65
|
+
reject(new Error('Failed to parse release info'));
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
}).on('error', reject);
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async function downloadFile(url, dest) {
|
|
73
|
+
return new Promise((resolve, reject) => {
|
|
74
|
+
const follow = (url) => {
|
|
75
|
+
https.get(url, (res) => {
|
|
76
|
+
if (res.statusCode === 302 || res.statusCode === 301) {
|
|
77
|
+
follow(res.headers.location);
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (res.statusCode !== 200) {
|
|
82
|
+
reject(new Error(`Download failed with status ${res.statusCode}`));
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const file = createWriteStream(dest);
|
|
87
|
+
res.pipe(file);
|
|
88
|
+
file.on('finish', () => {
|
|
89
|
+
file.close();
|
|
90
|
+
resolve();
|
|
91
|
+
});
|
|
92
|
+
}).on('error', reject);
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
follow(url);
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
async function extractTarGz(file, dest) {
|
|
100
|
+
await execAsync(`tar -xzf "${file}" -C "${dest}"`);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
async function extractZip(file, dest) {
|
|
104
|
+
if (process.platform === 'win32') {
|
|
105
|
+
await execAsync(`powershell -Command "Expand-Archive -Path '${file}' -DestinationPath '${dest}'"`);
|
|
106
|
+
} else {
|
|
107
|
+
await execAsync(`unzip -o "${file}" -d "${dest}"`);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
async function main() {
|
|
112
|
+
const binDir = path.join(packageRoot, 'bin');
|
|
113
|
+
const binaryPath = path.join(binDir, process.platform === 'win32' ? 'slvsx.exe' : 'slvsx');
|
|
114
|
+
|
|
115
|
+
// Skip if binary already exists
|
|
116
|
+
if (fs.existsSync(binaryPath)) {
|
|
117
|
+
console.log('slvsx binary already installed.');
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const platformInfo = getPlatformInfo();
|
|
122
|
+
if (!platformInfo) {
|
|
123
|
+
console.error(`Unsupported platform: ${process.platform}-${process.arch}`);
|
|
124
|
+
console.error('Please install slvsx manually from https://github.com/snoble/slvsx-cli/releases');
|
|
125
|
+
process.exit(0); // Don't fail install, just warn
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
console.log(`Installing slvsx for ${process.platform}-${process.arch}...`);
|
|
129
|
+
|
|
130
|
+
try {
|
|
131
|
+
const release = await getLatestRelease();
|
|
132
|
+
|
|
133
|
+
if (!release.assets) {
|
|
134
|
+
console.error('No release assets found. Please install slvsx manually.');
|
|
135
|
+
process.exit(0);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Find the right asset
|
|
139
|
+
const asset = release.assets.find(a =>
|
|
140
|
+
a.name.includes(platformInfo.assetPattern) &&
|
|
141
|
+
(a.name.endsWith('.tar.gz') || a.name.endsWith('.zip'))
|
|
142
|
+
);
|
|
143
|
+
|
|
144
|
+
if (!asset) {
|
|
145
|
+
console.error(`No binary found for ${process.platform}-${process.arch} (looking for '${platformInfo.assetPattern}')`);
|
|
146
|
+
console.error('Available assets:', release.assets.map(a => a.name).join(', '));
|
|
147
|
+
console.error('Please install slvsx manually from https://github.com/snoble/slvsx-cli/releases');
|
|
148
|
+
process.exit(0);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Create bin directory
|
|
152
|
+
fs.mkdirSync(binDir, { recursive: true });
|
|
153
|
+
|
|
154
|
+
// Download
|
|
155
|
+
const tempFile = path.join(binDir, asset.name);
|
|
156
|
+
console.log(`Downloading ${asset.name}...`);
|
|
157
|
+
await downloadFile(asset.browser_download_url, tempFile);
|
|
158
|
+
|
|
159
|
+
// Extract
|
|
160
|
+
console.log('Extracting...');
|
|
161
|
+
if (asset.name.endsWith('.tar.gz')) {
|
|
162
|
+
await extractTarGz(tempFile, binDir);
|
|
163
|
+
} else {
|
|
164
|
+
await extractZip(tempFile, binDir);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Clean up archive
|
|
168
|
+
fs.unlinkSync(tempFile);
|
|
169
|
+
|
|
170
|
+
// Make executable on Unix
|
|
171
|
+
if (process.platform !== 'win32') {
|
|
172
|
+
fs.chmodSync(binaryPath, 0o755);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Verify
|
|
176
|
+
if (fs.existsSync(binaryPath)) {
|
|
177
|
+
console.log(`✓ slvsx installed successfully to ${binaryPath}`);
|
|
178
|
+
} else {
|
|
179
|
+
// Binary might be in a subdirectory after extraction
|
|
180
|
+
const files = fs.readdirSync(binDir);
|
|
181
|
+
console.log('Extracted files:', files);
|
|
182
|
+
console.error('Binary not found at expected location. Please check extraction.');
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
} catch (error) {
|
|
186
|
+
console.error('Failed to download slvsx:', error.message);
|
|
187
|
+
console.error('Please install manually from https://github.com/snoble/slvsx-cli/releases');
|
|
188
|
+
process.exit(0); // Don't fail the npm install
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
main();
|
|
193
|
+
|