@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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/mcpServer.js +68 -63
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@context-engine-bridge/context-engine-mcp-bridge",
3
- "version": "0.0.34",
3
+ "version": "0.0.36",
4
4
  "description": "Context Engine MCP bridge (http/stdio proxy combining indexer + memory servers)",
5
5
  "bin": {
6
6
  "ctxce": "bin/ctxce.js",
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
- if (_isAbsolutePath(obj.path)) {
123
- if (workspace) {
124
- const contained = _resolveAndContain(obj.path, workspace);
125
- if (contained) paths.add(contained);
126
- } else {
127
- paths.add(obj.path);
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 { return result; }
183
- if (!parsed.ok) return result;
184
- if (_lspCircuitOpen()) {
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 bestLspMatch = null;
665
- let bestLspTs = 0;
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
- if (conn.created_at && (now - conn.created_at) > LSP_CONN_MAX_AGE) {
686
- } else {
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 > bestLspTs) {
689
- bestLspTs = connTs;
690
- bestLspMatch = wp;
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 (bestLspMatch) {
698
+ if (bestMatch) {
708
699
  debugLog("[ctxce] Resolved workspace from active LSP connection");
709
- return bestLspMatch;
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
- if (args && typeof args === "object" && args.include_lsp === true && _LSP_ENRICHABLE_TOOLS.has(name)) {
1177
- includeLsp = true;
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 (lspConn) finalResult = await _enrichWithLsp(finalResult, lspConn, workspace);
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;