@context-engine-bridge/context-engine-mcp-bridge 0.0.34 → 0.0.36
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/package.json +1 -1
- package/src/mcpServer.js +68 -63
package/package.json
CHANGED
package/src/mcpServer.js
CHANGED
|
@@ -118,21 +118,19 @@ function _extractPaths(obj, paths, workspace, depth = 0) {
|
|
|
118
118
|
for (const item of obj) _extractPaths(item, paths, workspace, depth + 1);
|
|
119
119
|
return;
|
|
120
120
|
}
|
|
121
|
+
let rawPath = null;
|
|
121
122
|
if (typeof obj.path === "string" && obj.path.length > 0) {
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
} else if (workspace) {
|
|
130
|
-
const contained = _resolveAndContain(obj.path, workspace);
|
|
123
|
+
rawPath = obj.path;
|
|
124
|
+
} else if (typeof obj.rel_path === "string" && obj.rel_path.length > 0) {
|
|
125
|
+
rawPath = obj.rel_path;
|
|
126
|
+
}
|
|
127
|
+
if (rawPath) {
|
|
128
|
+
if (workspace) {
|
|
129
|
+
const contained = _resolveAndContain(rawPath, workspace);
|
|
131
130
|
if (contained) paths.add(contained);
|
|
131
|
+
} else if (_isAbsolutePath(rawPath)) {
|
|
132
|
+
paths.add(rawPath);
|
|
132
133
|
}
|
|
133
|
-
} else if (typeof obj.rel_path === "string" && obj.rel_path.length > 0 && workspace) {
|
|
134
|
-
const contained = _resolveAndContain(obj.rel_path, workspace);
|
|
135
|
-
if (contained) paths.add(contained);
|
|
136
134
|
}
|
|
137
135
|
for (const [key, val] of Object.entries(obj)) {
|
|
138
136
|
if (key === "__proto__" || key === "constructor" || key === "prototype") continue;
|
|
@@ -173,37 +171,25 @@ function _callLspHandler(port, secret, operation, params) {
|
|
|
173
171
|
});
|
|
174
172
|
}
|
|
175
173
|
|
|
174
|
+
function _lspTarget(parsed) {
|
|
175
|
+
const r = parsed.result;
|
|
176
|
+
return r && typeof r === "object" && !Array.isArray(r) ? r : parsed;
|
|
177
|
+
}
|
|
178
|
+
|
|
176
179
|
async function _enrichWithLsp(result, lspConn, workspace) {
|
|
177
180
|
try {
|
|
181
|
+
debugLog("[ctxce] LSP enrich: workspace=" + workspace + " port=" + lspConn?.port);
|
|
178
182
|
if (!Array.isArray(result?.content)) return result;
|
|
179
183
|
const textBlock = result.content.find(c => c.type === "text");
|
|
180
184
|
if (!textBlock?.text) return result;
|
|
181
185
|
let parsed;
|
|
182
|
-
try { parsed = JSON.parse(textBlock.text); } catch {
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
parsed._lsp_status = "circuit_open";
|
|
186
|
-
} else {
|
|
187
|
-
const paths = new Set();
|
|
188
|
-
_extractPaths(parsed, paths, workspace);
|
|
189
|
-
if (paths.size === 0) {
|
|
190
|
-
parsed._lsp_status = "no_paths";
|
|
191
|
-
} else {
|
|
192
|
-
const diag = await _callLspHandler(lspConn.port, lspConn.secret, "diagnostics_recent", { paths: [...paths] });
|
|
193
|
-
if (!diag) {
|
|
194
|
-
_lspCircuitRecordFailure();
|
|
195
|
-
parsed._lsp_status = "connection_failed";
|
|
196
|
-
} else {
|
|
197
|
-
_lspCircuitRecordSuccess();
|
|
198
|
-
if (!diag.ok || !diag.files || diag.total === 0) {
|
|
199
|
-
parsed._lsp_status = "no_diagnostics";
|
|
200
|
-
} else {
|
|
201
|
-
parsed._lsp = { diagnostics: diag.files, total: diag.total };
|
|
202
|
-
parsed._lsp_status = "ok";
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
}
|
|
186
|
+
try { parsed = JSON.parse(textBlock.text); } catch (e) {
|
|
187
|
+
debugLog("[ctxce] LSP enrich: JSON parse failed: " + String(e));
|
|
188
|
+
return result;
|
|
206
189
|
}
|
|
190
|
+
if (!parsed.ok) return result;
|
|
191
|
+
const target = _lspTarget(parsed);
|
|
192
|
+
target._lsp_status = await _resolveLspStatus(target, lspConn, workspace);
|
|
207
193
|
textBlock.text = JSON.stringify(parsed);
|
|
208
194
|
return result;
|
|
209
195
|
} catch (err) {
|
|
@@ -213,6 +199,22 @@ async function _enrichWithLsp(result, lspConn, workspace) {
|
|
|
213
199
|
}
|
|
214
200
|
}
|
|
215
201
|
|
|
202
|
+
async function _resolveLspStatus(target, lspConn, workspace) {
|
|
203
|
+
if (_lspCircuitOpen()) return "circuit_open";
|
|
204
|
+
const paths = new Set();
|
|
205
|
+
_extractPaths(target, paths, workspace);
|
|
206
|
+
if (paths.size === 0) return "no_paths";
|
|
207
|
+
const diag = await _callLspHandler(lspConn.port, lspConn.secret, "diagnostics_recent", { paths: [...paths] });
|
|
208
|
+
if (!diag) {
|
|
209
|
+
_lspCircuitRecordFailure();
|
|
210
|
+
return "connection_failed";
|
|
211
|
+
}
|
|
212
|
+
_lspCircuitRecordSuccess();
|
|
213
|
+
if (!diag.ok || !diag.files || diag.total === 0) return "no_diagnostics";
|
|
214
|
+
target._lsp = { diagnostics: diag.files, total: diag.total };
|
|
215
|
+
return "ok";
|
|
216
|
+
}
|
|
217
|
+
|
|
216
218
|
function debugLog(message) {
|
|
217
219
|
try {
|
|
218
220
|
const text = typeof message === "string" ? message : String(message);
|
|
@@ -661,10 +663,8 @@ function _resolveWorkspace(providedWorkspace) {
|
|
|
661
663
|
const wsRoot = path.join(os.homedir(), ".ctxce", "workspaces");
|
|
662
664
|
try {
|
|
663
665
|
const dirs = fs.readdirSync(wsRoot).slice(0, MAX_WS_SCAN);
|
|
664
|
-
let
|
|
665
|
-
let
|
|
666
|
-
let bestMetaMatch = null;
|
|
667
|
-
let bestMetaTs = 0;
|
|
666
|
+
let bestMatch = null;
|
|
667
|
+
let bestTs = 0;
|
|
668
668
|
for (const dir of dirs) {
|
|
669
669
|
if (dir === "." || dir === ".." || dir.includes("/") || dir.includes("\0")) continue;
|
|
670
670
|
const dirPath = path.join(wsRoot, dir);
|
|
@@ -682,35 +682,22 @@ function _resolveWorkspace(providedWorkspace) {
|
|
|
682
682
|
if (conn.pid && conn.port && _isValidPort(conn.port)) {
|
|
683
683
|
try {
|
|
684
684
|
process.kill(conn.pid, 0);
|
|
685
|
-
|
|
686
|
-
|
|
685
|
+
const notExpired = !conn.created_at || (now - conn.created_at) <= LSP_CONN_MAX_AGE;
|
|
686
|
+
if (notExpired) {
|
|
687
687
|
const connTs = typeof conn.created_at === "number" ? conn.created_at : 0;
|
|
688
|
-
if (connTs >
|
|
689
|
-
|
|
690
|
-
|
|
688
|
+
if (connTs > bestTs) {
|
|
689
|
+
bestTs = connTs;
|
|
690
|
+
bestMatch = wp;
|
|
691
691
|
}
|
|
692
692
|
}
|
|
693
693
|
} catch (_) {}
|
|
694
694
|
}
|
|
695
695
|
} catch (_) {}
|
|
696
|
-
const updatedAt = typeof meta.updated_at === "number"
|
|
697
|
-
? meta.updated_at
|
|
698
|
-
: typeof meta.updated_at === "string"
|
|
699
|
-
? new Date(meta.updated_at).getTime() || 0
|
|
700
|
-
: 0;
|
|
701
|
-
if (updatedAt > bestMetaTs) {
|
|
702
|
-
bestMetaTs = updatedAt;
|
|
703
|
-
bestMetaMatch = wp;
|
|
704
|
-
}
|
|
705
696
|
} catch (_) {}
|
|
706
697
|
}
|
|
707
|
-
if (
|
|
698
|
+
if (bestMatch) {
|
|
708
699
|
debugLog("[ctxce] Resolved workspace from active LSP connection");
|
|
709
|
-
return
|
|
710
|
-
}
|
|
711
|
-
if (bestMetaMatch) {
|
|
712
|
-
debugLog("[ctxce] Resolved workspace from most recent meta.json");
|
|
713
|
-
return bestMetaMatch;
|
|
700
|
+
return bestMatch;
|
|
714
701
|
}
|
|
715
702
|
} catch (_) {}
|
|
716
703
|
|
|
@@ -1173,11 +1160,14 @@ async function createBridgeServer(options) {
|
|
|
1173
1160
|
let args = params.arguments;
|
|
1174
1161
|
|
|
1175
1162
|
let includeLsp = false;
|
|
1176
|
-
|
|
1177
|
-
|
|
1163
|
+
const rawLsp = args && typeof args === "object" ? args.include_lsp : undefined;
|
|
1164
|
+
if (rawLsp !== undefined) {
|
|
1178
1165
|
const { include_lsp: _stripped, ...rest } = args;
|
|
1179
1166
|
args = rest;
|
|
1180
1167
|
}
|
|
1168
|
+
if ((rawLsp === true || rawLsp === "true" || rawLsp === 1 || rawLsp === "1") && _LSP_ENRICHABLE_TOOLS.has(name)) {
|
|
1169
|
+
includeLsp = true;
|
|
1170
|
+
}
|
|
1181
1171
|
|
|
1182
1172
|
debugLog(`[ctxce] tools/call: ${name || "<no-name>"}`);
|
|
1183
1173
|
|
|
@@ -1304,7 +1294,22 @@ async function createBridgeServer(options) {
|
|
|
1304
1294
|
);
|
|
1305
1295
|
let finalResult = maybeRemapToolResult(name, result, workspace);
|
|
1306
1296
|
const lspConn = includeLsp && _readLspConnection(workspace);
|
|
1307
|
-
if (
|
|
1297
|
+
if (includeLsp) {
|
|
1298
|
+
debugLog("[ctxce] LSP gate: tool=" + name + " lsp=" + (lspConn ? "port:" + lspConn.port : "null"));
|
|
1299
|
+
}
|
|
1300
|
+
if (lspConn) {
|
|
1301
|
+
finalResult = await _enrichWithLsp(finalResult, lspConn, workspace);
|
|
1302
|
+
} else if (includeLsp) {
|
|
1303
|
+
try {
|
|
1304
|
+
const tb = Array.isArray(finalResult?.content) && finalResult.content.find(c => c.type === "text");
|
|
1305
|
+
if (tb?.text) {
|
|
1306
|
+
const p = JSON.parse(tb.text);
|
|
1307
|
+
const t = _lspTarget(p);
|
|
1308
|
+
t._lsp_status = "not_available";
|
|
1309
|
+
tb.text = JSON.stringify(p);
|
|
1310
|
+
}
|
|
1311
|
+
} catch (e) { debugLog("[ctxce] LSP not_available inject failed: " + String(e)); }
|
|
1312
|
+
}
|
|
1308
1313
|
return finalResult;
|
|
1309
1314
|
} catch (err) {
|
|
1310
1315
|
lastError = err;
|