@softtechai/quickmcp 1.1.0 → 1.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/README.md +3 -3
- package/package.json +2 -2
- package/quickmcp-direct-stdio.js +72 -22
package/README.md
CHANGED
|
@@ -479,7 +479,7 @@ QuickMCP, NPM üzerinden çalıştırıldığında artık varsayılan olarak web
|
|
|
479
479
|
### Hızlı başlatma
|
|
480
480
|
|
|
481
481
|
```bash
|
|
482
|
-
npx -y @
|
|
482
|
+
npx -y @softtechai/quickmcp
|
|
483
483
|
# UI -> http://localhost:3000
|
|
484
484
|
# Integrated MCP sidecar -> :3001
|
|
485
485
|
```
|
|
@@ -493,7 +493,7 @@ npx -y @softtech/quickmcp
|
|
|
493
493
|
Örnek:
|
|
494
494
|
|
|
495
495
|
```bash
|
|
496
|
-
npx -y @
|
|
496
|
+
npx -y @softtechai/quickmcp --port=4000 --data-dir=./data
|
|
497
497
|
```
|
|
498
498
|
|
|
499
499
|
### Ortam değişkenleri
|
|
@@ -506,7 +506,7 @@ npx -y @softtech/quickmcp --port=4000 --data-dir=./data
|
|
|
506
506
|
Örnek:
|
|
507
507
|
|
|
508
508
|
```bash
|
|
509
|
-
PORT=4000 QUICKMCP_DATA_DIR=./data npx -y @
|
|
509
|
+
PORT=4000 QUICKMCP_DATA_DIR=./data npx -y @softtechai/quickmcp
|
|
510
510
|
```
|
|
511
511
|
|
|
512
512
|
> Not: Web UI, stdio tabanlı MCP sunucusunun yanında yan servis olarak çalışır; Claude Desktop entegrasyonu ile çakışmaz.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@softtechai/quickmcp",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.2",
|
|
4
4
|
"description": "An application to generate MCP servers from various data sources and test them",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -40,6 +40,7 @@
|
|
|
40
40
|
"better-sqlite3": "^12.4.1",
|
|
41
41
|
"cors": "^2.8.5",
|
|
42
42
|
"csv-parser": "^3.0.0",
|
|
43
|
+
"dotenv": "^16.4.5",
|
|
43
44
|
"exceljs": "^4.4.0",
|
|
44
45
|
"express": "^4.18.0",
|
|
45
46
|
"imapflow": "^1.2.6",
|
|
@@ -63,7 +64,6 @@
|
|
|
63
64
|
"@types/ws": "^8.5.0",
|
|
64
65
|
"@typescript-eslint/eslint-plugin": "^6.0.0",
|
|
65
66
|
"@typescript-eslint/parser": "^6.0.0",
|
|
66
|
-
"dotenv": "^16.4.5",
|
|
67
67
|
"eslint": "^8.0.0",
|
|
68
68
|
"tsx": "^4.0.0",
|
|
69
69
|
"typescript": "^5.0.0"
|
package/quickmcp-direct-stdio.js
CHANGED
|
@@ -4,12 +4,19 @@ const path = require('path');
|
|
|
4
4
|
const fs = require('fs');
|
|
5
5
|
const os = require('os');
|
|
6
6
|
const net = require('net');
|
|
7
|
-
|
|
7
|
+
let dotenv = null;
|
|
8
|
+
try {
|
|
9
|
+
dotenv = require('dotenv');
|
|
10
|
+
} catch (_) {
|
|
11
|
+
dotenv = null;
|
|
12
|
+
}
|
|
8
13
|
|
|
9
|
-
|
|
10
|
-
//
|
|
11
|
-
|
|
12
|
-
dotenv.config();
|
|
14
|
+
if (dotenv) {
|
|
15
|
+
// Load .env from script directory first (works with Claude Desktop custom CWD),
|
|
16
|
+
// then fallback to process CWD.
|
|
17
|
+
dotenv.config({ path: path.join(__dirname, '.env') });
|
|
18
|
+
dotenv.config();
|
|
19
|
+
}
|
|
13
20
|
|
|
14
21
|
let logger;
|
|
15
22
|
try {
|
|
@@ -73,6 +80,16 @@ const wantsWeb = argv.includes('--web') || argv.includes('-w') || argv.includes(
|
|
|
73
80
|
const noWeb = argv.includes('--no-web');
|
|
74
81
|
const portArg = argv.find(a => a.startsWith('--port='));
|
|
75
82
|
const dataDirArg = argv.find(a => a.startsWith('--data-dir='));
|
|
83
|
+
|
|
84
|
+
function isProcessAlive(pid) {
|
|
85
|
+
if (!Number.isInteger(pid) || pid <= 0) return false;
|
|
86
|
+
try {
|
|
87
|
+
process.kill(pid, 0);
|
|
88
|
+
return true;
|
|
89
|
+
} catch (_) {
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
76
93
|
// Default behavior: Web UI enabled unless explicitly disabled
|
|
77
94
|
if (noWeb || process.env.QUICKMCP_ENABLE_WEB === '0' || process.env.QUICKMCP_DISABLE_WEB === '1') {
|
|
78
95
|
process.env.QUICKMCP_ENABLE_WEB = '0';
|
|
@@ -149,38 +166,69 @@ if (process.env.QUICKMCP_ENABLE_WEB === '1') {
|
|
|
149
166
|
const preferredPort = parseInt(process.env.PORT || '3000', 10);
|
|
150
167
|
const lockPath = path.join(runDir, `ui-${preferredPort}.lock`);
|
|
151
168
|
let hasLock = false;
|
|
152
|
-
try {
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
169
|
+
const cleanup = () => { try { fs.unlinkSync(lockPath); } catch {} };
|
|
170
|
+
const tryAcquireLock = () => {
|
|
171
|
+
try {
|
|
172
|
+
const fd = fs.openSync(lockPath, 'wx');
|
|
173
|
+
fs.writeFileSync(fd, String(process.pid), 'utf8');
|
|
174
|
+
fs.closeSync(fd);
|
|
175
|
+
hasLock = true;
|
|
176
|
+
process.once('exit', cleanup);
|
|
177
|
+
process.once('SIGINT', () => { cleanup(); process.exit(0); });
|
|
178
|
+
process.once('SIGTERM', () => { cleanup(); process.exit(0); });
|
|
179
|
+
} catch (e) {
|
|
180
|
+
if (e && e.code === 'EEXIST') {
|
|
181
|
+
let ownerPid = NaN;
|
|
182
|
+
try {
|
|
183
|
+
ownerPid = parseInt(String(fs.readFileSync(lockPath, 'utf8')).trim(), 10);
|
|
184
|
+
} catch (_) {}
|
|
185
|
+
|
|
186
|
+
if (!isProcessAlive(ownerPid)) {
|
|
187
|
+
// Lock file exists but owner is gone (or lock format is empty/invalid): reclaim it.
|
|
188
|
+
try {
|
|
189
|
+
fs.unlinkSync(lockPath);
|
|
190
|
+
const fd = fs.openSync(lockPath, 'wx');
|
|
191
|
+
fs.writeFileSync(fd, String(process.pid), 'utf8');
|
|
192
|
+
fs.closeSync(fd);
|
|
193
|
+
hasLock = true;
|
|
194
|
+
process.once('exit', cleanup);
|
|
195
|
+
process.once('SIGINT', () => { cleanup(); process.exit(0); });
|
|
196
|
+
process.once('SIGTERM', () => { cleanup(); process.exit(0); });
|
|
197
|
+
logger.error(`[QuickMCP] Reclaimed stale UI lock: ${lockPath}`);
|
|
198
|
+
return;
|
|
199
|
+
} catch (reclaimErr) {
|
|
200
|
+
logger.error('[QuickMCP] Failed to reclaim stale UI lock (continuing without UI):', reclaimErr && reclaimErr.message ? reclaimErr.message : reclaimErr);
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
logger.error(`[QuickMCP] UI lock held by pid ${ownerPid}; skipping Web UI in this process`);
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
logger.error('[QuickMCP] UI lock error (continuing without UI):', e && e.message ? e.message : e);
|
|
163
210
|
}
|
|
164
|
-
|
|
165
|
-
|
|
211
|
+
};
|
|
212
|
+
tryAcquireLock();
|
|
166
213
|
|
|
167
214
|
if (!hasLock) {
|
|
168
|
-
logger.error('[QuickMCP] UI
|
|
215
|
+
logger.error('[QuickMCP] Web UI not started in this process');
|
|
169
216
|
} else {
|
|
170
217
|
// Only start Web UI if preferred port is actually free.
|
|
171
218
|
const probe = net.createServer();
|
|
172
219
|
probe.once('error', (err) => {
|
|
173
220
|
if (err && (err.code === 'EADDRINUSE' || err.code === 'EACCES')) {
|
|
174
221
|
// Port is busy or not permitted; skip starting Web UI to avoid crashing Claude session
|
|
222
|
+
logger.error(`[QuickMCP] Web UI port ${preferredPort} unavailable (${err.code}); skipping Web UI`);
|
|
175
223
|
try { fs.unlinkSync(lockPath); } catch {}
|
|
176
224
|
return;
|
|
177
225
|
}
|
|
178
226
|
// For other errors, still try starting server; better to attempt than silently skip for unknown cases
|
|
179
|
-
safeStartWebServer();
|
|
227
|
+
safeStartWebServer(preferredPort);
|
|
180
228
|
});
|
|
181
229
|
probe.once('listening', () => {
|
|
182
230
|
probe.close(() => {
|
|
183
|
-
safeStartWebServer();
|
|
231
|
+
safeStartWebServer(preferredPort);
|
|
184
232
|
});
|
|
185
233
|
});
|
|
186
234
|
try {
|
|
@@ -196,12 +244,13 @@ if (process.env.QUICKMCP_ENABLE_WEB === '1') {
|
|
|
196
244
|
}
|
|
197
245
|
|
|
198
246
|
// Start the web server but swallow a race-condition EADDRINUSE from Express listen
|
|
199
|
-
function safeStartWebServer() {
|
|
247
|
+
function safeStartWebServer(port) {
|
|
200
248
|
let handled = false;
|
|
201
249
|
const handler = (err) => {
|
|
202
250
|
if (!handled && err && err.code === 'EADDRINUSE' && err.syscall === 'listen') {
|
|
203
251
|
handled = true;
|
|
204
252
|
// Swallow this once so the STDIO server keeps running; another instance already owns :3000
|
|
253
|
+
logger.error(`[QuickMCP] Web UI port ${port || process.env.PORT || 3000} became busy during startup; continuing without UI`);
|
|
205
254
|
process.removeListener('uncaughtException', handler);
|
|
206
255
|
return;
|
|
207
256
|
}
|
|
@@ -212,6 +261,7 @@ function safeStartWebServer() {
|
|
|
212
261
|
// Install one-time handler to catch immediate async throw from Express
|
|
213
262
|
process.prependOnceListener('uncaughtException', handler);
|
|
214
263
|
try {
|
|
264
|
+
logger.error(`[QuickMCP] Starting Web UI at http://localhost:${port || process.env.PORT || 3000}`);
|
|
215
265
|
require('./dist/server/server.js');
|
|
216
266
|
} catch (e) {
|
|
217
267
|
// Synchronous load error
|