@context-engine-bridge/context-engine-mcp-bridge 0.0.7 → 0.0.8

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@context-engine-bridge/context-engine-mcp-bridge",
3
- "version": "0.0.7",
3
+ "version": "0.0.8",
4
4
  "description": "Context Engine MCP bridge (http/stdio proxy combining indexer + memory servers)",
5
5
  "bin": {
6
6
  "ctxce": "bin/ctxce.js",
@@ -55,17 +55,75 @@ function computeWorkspaceRelativePath(containerPath, hostPath) {
55
55
  }
56
56
  }
57
57
 
58
+ function remapRelatedPathToClient(p, workspaceRoot) {
59
+ try {
60
+ const s = typeof p === "string" ? p : "";
61
+ const root = typeof workspaceRoot === "string" ? workspaceRoot : "";
62
+ if (!s || !root) {
63
+ return p;
64
+ }
65
+
66
+ const sNorm = s.replace(/\\/g, path.sep);
67
+ if (sNorm.startsWith(root + path.sep) || sNorm === root) {
68
+ return sNorm;
69
+ }
70
+
71
+ if (s.startsWith("/work/")) {
72
+ const rest = s.slice("/work/".length);
73
+ const parts = rest.split("/").filter(Boolean);
74
+ if (parts.length >= 2) {
75
+ const rel = parts.slice(1).join("/");
76
+ const relNative = _posixToNative(rel);
77
+ return path.join(root, relNative);
78
+ }
79
+ return p;
80
+ }
81
+
82
+ // If it's already a relative path, join it to the workspace root.
83
+ if (!s.startsWith("/") && !s.includes(":") && !s.includes("\\")) {
84
+ const relPosix = s.trim();
85
+ if (relPosix && relPosix !== "." && !relPosix.startsWith("../") && relPosix !== "..") {
86
+ const relNative = _posixToNative(relPosix);
87
+ const joined = path.join(root, relNative);
88
+ const relCheck = path.relative(root, joined);
89
+ if (relCheck && !relCheck.startsWith(`..${path.sep}`) && relCheck !== "..") {
90
+ return joined;
91
+ }
92
+ }
93
+ }
94
+
95
+ return p;
96
+ } catch {
97
+ return p;
98
+ }
99
+ }
100
+
58
101
  function remapHitPaths(hit, workspaceRoot) {
59
102
  if (!hit || typeof hit !== "object") {
60
103
  return hit;
61
104
  }
62
- const hostPath = typeof hit.host_path === "string" ? hit.host_path : "";
63
- const containerPath = typeof hit.container_path === "string" ? hit.container_path : "";
105
+ const rawPath = typeof hit.path === "string" ? hit.path : "";
106
+ let hostPath = typeof hit.host_path === "string" ? hit.host_path : "";
107
+ let containerPath = typeof hit.container_path === "string" ? hit.container_path : "";
108
+ if (!hostPath && rawPath) {
109
+ hostPath = rawPath;
110
+ }
111
+ if (!containerPath && rawPath) {
112
+ containerPath = rawPath;
113
+ }
64
114
  const relPath = computeWorkspaceRelativePath(containerPath, hostPath);
65
115
  const out = { ...hit };
66
116
  if (relPath) {
67
117
  out.rel_path = relPath;
68
118
  }
119
+ // Remap related_paths nested under each hit (repo_search/hybrid_search emit this per result).
120
+ try {
121
+ if (Array.isArray(out.related_paths)) {
122
+ out.related_paths = out.related_paths.map((p) => remapRelatedPathToClient(p, workspaceRoot));
123
+ }
124
+ } catch {
125
+ // ignore
126
+ }
69
127
  if (workspaceRoot && relPath) {
70
128
  try {
71
129
  const relNative = _posixToNative(relPath);
@@ -107,18 +165,42 @@ function remapHitPaths(hit, workspaceRoot) {
107
165
  }
108
166
  }
109
167
  const overridePath = envTruthy(process.env.CTXCE_BRIDGE_OVERRIDE_PATH, true);
110
- if (overridePath && relPath) {
111
- out.path = relPath;
168
+ if (overridePath) {
169
+ if (typeof out.client_path === "string" && out.client_path) {
170
+ out.path = out.client_path;
171
+ } else if (relPath) {
172
+ out.path = relPath;
173
+ }
112
174
  }
113
175
  return out;
114
176
  }
115
177
 
116
- function remapStringPath(p) {
178
+ function remapStringPath(p, workspaceRoot) {
117
179
  try {
118
180
  const s = typeof p === "string" ? p : "";
119
181
  if (!s) {
120
182
  return p;
121
183
  }
184
+ // If this is already a path within the current client workspace, rewrite to a
185
+ // workspace-relative string when override is enabled.
186
+ try {
187
+ const root = typeof workspaceRoot === "string" ? workspaceRoot : "";
188
+ if (root) {
189
+ const sNorm = s.replace(/\\/g, path.sep);
190
+ if (sNorm.startsWith(root + path.sep) || sNorm === root) {
191
+ const relNative = path.relative(root, sNorm);
192
+ const relPosix = String(relNative).split(path.sep).join("/");
193
+ if (relPosix && !relPosix.startsWith("../") && relPosix !== "..") {
194
+ const override = envTruthy(process.env.CTXCE_BRIDGE_OVERRIDE_PATH, true);
195
+ if (override) {
196
+ return relPosix;
197
+ }
198
+ }
199
+ }
200
+ }
201
+ } catch {
202
+ // ignore
203
+ }
122
204
  if (s.startsWith("/work/")) {
123
205
  const rest = s.slice("/work/".length);
124
206
  const parts = rest.split("/").filter(Boolean);
@@ -191,18 +273,7 @@ function applyPathMappingToPayload(payload, workspaceRoot) {
191
273
  out.citations = mapHitsArray(out.citations);
192
274
  }
193
275
  if (Array.isArray(out.related_paths)) {
194
- out.related_paths = out.related_paths.map((p) => remapStringPath(p));
195
- }
196
-
197
- // context_search: {results:[{source:"code"|"memory", ...}]}
198
- if (Array.isArray(out.results)) {
199
- out.results = out.results.map((r) => {
200
- if (!r || typeof r !== "object") {
201
- return r;
202
- }
203
- // Only code results have path-like fields
204
- return remapHitPaths(r, workspaceRoot);
205
- });
276
+ out.related_paths = out.related_paths.map((p) => remapRelatedPathToClient(p, workspaceRoot));
206
277
  }
207
278
 
208
279
  // Some tools nest under {result:{...}}
@@ -242,24 +313,25 @@ export function maybeRemapToolResult(name, result, workspaceRoot) {
242
313
  }
243
314
 
244
315
  const mapped = applyPathMappingToPayload(parsed.value, workspaceRoot);
316
+ let outResult = result;
245
317
  if (parsed.mode === "structured") {
246
- return { ...result, structuredContent: mapped };
318
+ outResult = { ...result, structuredContent: mapped };
247
319
  }
248
320
 
249
321
  // Replace text payload for clients that only read `content[].text`
250
322
  try {
251
- const content = Array.isArray(result.content) ? result.content.slice() : [];
323
+ const content = Array.isArray(outResult.content) ? outResult.content.slice() : [];
252
324
  const idx = content.findIndex(
253
325
  (c) => c && c.type === "text" && typeof c.text === "string",
254
326
  );
255
327
  if (idx >= 0) {
256
328
  content[idx] = { ...content[idx], text: JSON.stringify(mapped) };
257
- return { ...result, content };
329
+ outResult = { ...outResult, content };
258
330
  }
259
331
  } catch {
260
332
  // ignore
261
333
  }
262
- return result;
334
+ return outResult;
263
335
  } catch {
264
336
  return result;
265
337
  }