@context-engine-bridge/context-engine-mcp-bridge 0.0.8 → 0.0.10
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 +1 -1
- package/package.json +1 -1
- package/src/mcpServer.js +3 -1
- package/src/resultPathMapping.js +189 -28
package/README.md
CHANGED
|
@@ -15,7 +15,7 @@ other MCP clients) as long as the Context Engine stack is running.
|
|
|
15
15
|
## Prerequisites
|
|
16
16
|
|
|
17
17
|
- Node.js **>= 18** (see `engines` in `package.json`).
|
|
18
|
-
- A running Context Engine stack (e.g. via `docker-compose.
|
|
18
|
+
- A running Context Engine stack (e.g. via `docker-compose.yml`) with:
|
|
19
19
|
- MCP indexer HTTP endpoint (default: `http://localhost:8003/mcp`).
|
|
20
20
|
- MCP memory HTTP endpoint (optional, default: `http://localhost:8002/mcp`).
|
|
21
21
|
- For optional auth:
|
package/package.json
CHANGED
package/src/mcpServer.js
CHANGED
|
@@ -10,7 +10,7 @@ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
|
10
10
|
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
11
11
|
import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
12
12
|
import { loadAnyAuthEntry, loadAuthEntry } from "./authConfig.js";
|
|
13
|
-
import { maybeRemapToolResult } from "./resultPathMapping.js";
|
|
13
|
+
import { maybeRemapToolArgs, maybeRemapToolResult } from "./resultPathMapping.js";
|
|
14
14
|
|
|
15
15
|
function debugLog(message) {
|
|
16
16
|
try {
|
|
@@ -503,6 +503,8 @@ async function createBridgeServer(options) {
|
|
|
503
503
|
args = obj;
|
|
504
504
|
}
|
|
505
505
|
|
|
506
|
+
args = maybeRemapToolArgs(name, args, workspace);
|
|
507
|
+
|
|
506
508
|
if (name === "set_session_defaults") {
|
|
507
509
|
const indexerResult = await indexerClient.callTool({ name, arguments: args });
|
|
508
510
|
if (memoryClient) {
|
package/src/resultPathMapping.js
CHANGED
|
@@ -28,18 +28,170 @@ function _posixToNative(rel) {
|
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
function
|
|
31
|
+
function _nativeToPosix(p) {
|
|
32
32
|
try {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
33
|
+
if (!p) {
|
|
34
|
+
return "";
|
|
35
|
+
}
|
|
36
|
+
return String(p).split(path.sep).join("/");
|
|
37
|
+
} catch {
|
|
38
|
+
return p;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function _workPathToRepoRelPosix(p) {
|
|
43
|
+
try {
|
|
44
|
+
const s = typeof p === "string" ? p.trim() : "";
|
|
45
|
+
if (!s || !s.startsWith("/work/")) {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
const rest = s.slice("/work/".length);
|
|
49
|
+
const parts = rest.split("/").filter(Boolean);
|
|
50
|
+
if (parts.length >= 2) {
|
|
51
|
+
return parts.slice(1).join("/");
|
|
52
|
+
}
|
|
53
|
+
if (parts.length === 1) {
|
|
54
|
+
return parts[0];
|
|
55
|
+
}
|
|
56
|
+
return "";
|
|
57
|
+
} catch {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function normalizeToolArgPath(p, workspaceRoot) {
|
|
63
|
+
try {
|
|
64
|
+
const s = typeof p === "string" ? p.trim() : "";
|
|
65
|
+
if (!s) {
|
|
66
|
+
return p;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const root = typeof workspaceRoot === "string" ? workspaceRoot : "";
|
|
70
|
+
const sPosix = s.replace(/\\/g, "/");
|
|
71
|
+
|
|
72
|
+
const fromWork = _workPathToRepoRelPosix(sPosix);
|
|
73
|
+
if (typeof fromWork === "string" && fromWork) {
|
|
74
|
+
return fromWork;
|
|
75
|
+
}
|
|
76
|
+
if (fromWork === "") {
|
|
77
|
+
return p;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (root) {
|
|
81
|
+
try {
|
|
82
|
+
const sNorm = s.replace(/\\/g, path.sep);
|
|
83
|
+
const rootNorm = root.replace(/\\/g, path.sep);
|
|
84
|
+
if (sNorm === rootNorm || sNorm.startsWith(rootNorm + path.sep)) {
|
|
85
|
+
const relNative = path.relative(rootNorm, sNorm);
|
|
86
|
+
const relPosix = _nativeToPosix(relNative);
|
|
87
|
+
if (relPosix && relPosix !== "." && relPosix !== ".." && !relPosix.startsWith("../")) {
|
|
88
|
+
return relPosix;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
} catch {
|
|
92
|
+
// ignore
|
|
93
|
+
}
|
|
94
|
+
try {
|
|
95
|
+
const base = path.posix.basename(root.replace(/\\/g, "/"));
|
|
96
|
+
if (base && sPosix.startsWith(base + "/")) {
|
|
97
|
+
const rest = sPosix.slice((base + "/").length);
|
|
98
|
+
if (rest && rest !== "." && rest !== ".." && !rest.startsWith("../")) {
|
|
99
|
+
return rest;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
} catch {
|
|
103
|
+
// ignore
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (sPosix.startsWith("./")) {
|
|
108
|
+
const rest = sPosix.slice(2);
|
|
109
|
+
if (rest && rest !== "." && rest !== ".." && !rest.startsWith("../")) {
|
|
110
|
+
return rest;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
if (sPosix === ".") {
|
|
114
|
+
return "";
|
|
115
|
+
}
|
|
116
|
+
return p;
|
|
117
|
+
} catch {
|
|
118
|
+
return p;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function normalizeToolArgGlob(p, workspaceRoot) {
|
|
123
|
+
try {
|
|
124
|
+
const s = typeof p === "string" ? p : "";
|
|
125
|
+
if (!s) {
|
|
126
|
+
return p;
|
|
127
|
+
}
|
|
128
|
+
// TODO(ctxce): If this becomes annoying, consider making glob normalization
|
|
129
|
+
// more conservative (e.g. only strip a repo prefix when followed by "/",
|
|
130
|
+
// and avoid collapsing "<repo>/**" into "**" which can broaden scope).
|
|
131
|
+
if (s.startsWith("!")) {
|
|
132
|
+
const rest = s.slice(1);
|
|
133
|
+
const mapped = normalizeToolArgPath(rest, workspaceRoot);
|
|
134
|
+
if (typeof mapped === "string") {
|
|
135
|
+
return "!" + mapped;
|
|
136
|
+
}
|
|
137
|
+
return p;
|
|
138
|
+
}
|
|
139
|
+
return normalizeToolArgPath(s, workspaceRoot);
|
|
140
|
+
} catch {
|
|
141
|
+
return p;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function applyPathMappingToArgs(value, workspaceRoot, keyHint = "") {
|
|
146
|
+
try {
|
|
147
|
+
if (value === null || value === undefined) {
|
|
148
|
+
return value;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const key = typeof keyHint === "string" ? keyHint : "";
|
|
152
|
+
const lowered = key.toLowerCase();
|
|
153
|
+
const shouldMapString =
|
|
154
|
+
lowered === "path" ||
|
|
155
|
+
lowered === "under" ||
|
|
156
|
+
lowered === "root" ||
|
|
157
|
+
lowered === "subdir" ||
|
|
158
|
+
lowered === "path_glob" ||
|
|
159
|
+
lowered === "not_glob";
|
|
160
|
+
|
|
161
|
+
if (typeof value === "string") {
|
|
162
|
+
if (!shouldMapString) {
|
|
163
|
+
return value;
|
|
39
164
|
}
|
|
40
|
-
if (
|
|
41
|
-
return
|
|
165
|
+
if (lowered === "path_glob" || lowered === "not_glob") {
|
|
166
|
+
return normalizeToolArgGlob(value, workspaceRoot);
|
|
42
167
|
}
|
|
168
|
+
return normalizeToolArgPath(value, workspaceRoot);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (Array.isArray(value)) {
|
|
172
|
+
return value.map((v) => applyPathMappingToArgs(v, workspaceRoot, keyHint));
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (typeof value === "object") {
|
|
176
|
+
const out = { ...value };
|
|
177
|
+
for (const [k, v] of Object.entries(out)) {
|
|
178
|
+
out[k] = applyPathMappingToArgs(v, workspaceRoot, k);
|
|
179
|
+
}
|
|
180
|
+
return out;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
return value;
|
|
184
|
+
} catch {
|
|
185
|
+
return value;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function computeWorkspaceRelativePath(containerPath, hostPath) {
|
|
190
|
+
try {
|
|
191
|
+
const cont = typeof containerPath === "string" ? containerPath.trim() : "";
|
|
192
|
+
const rel = _workPathToRepoRelPosix(cont);
|
|
193
|
+
if (typeof rel === "string" && rel) {
|
|
194
|
+
return rel;
|
|
43
195
|
}
|
|
44
196
|
} catch {
|
|
45
197
|
}
|
|
@@ -68,15 +220,10 @@ function remapRelatedPathToClient(p, workspaceRoot) {
|
|
|
68
220
|
return sNorm;
|
|
69
221
|
}
|
|
70
222
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
const
|
|
74
|
-
|
|
75
|
-
const rel = parts.slice(1).join("/");
|
|
76
|
-
const relNative = _posixToNative(rel);
|
|
77
|
-
return path.join(root, relNative);
|
|
78
|
-
}
|
|
79
|
-
return p;
|
|
223
|
+
const rel = _workPathToRepoRelPosix(s);
|
|
224
|
+
if (typeof rel === "string" && rel) {
|
|
225
|
+
const relNative = _posixToNative(rel);
|
|
226
|
+
return path.join(root, relNative);
|
|
80
227
|
}
|
|
81
228
|
|
|
82
229
|
// If it's already a relative path, join it to the workspace root.
|
|
@@ -201,17 +348,13 @@ function remapStringPath(p, workspaceRoot) {
|
|
|
201
348
|
} catch {
|
|
202
349
|
// ignore
|
|
203
350
|
}
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
const
|
|
207
|
-
if (
|
|
208
|
-
|
|
209
|
-
const override = envTruthy(process.env.CTXCE_BRIDGE_OVERRIDE_PATH, true);
|
|
210
|
-
if (override) {
|
|
211
|
-
return rel;
|
|
212
|
-
}
|
|
213
|
-
return p;
|
|
351
|
+
const rel = _workPathToRepoRelPosix(s);
|
|
352
|
+
if (typeof rel === "string" && rel) {
|
|
353
|
+
const override = envTruthy(process.env.CTXCE_BRIDGE_OVERRIDE_PATH, true);
|
|
354
|
+
if (override) {
|
|
355
|
+
return rel;
|
|
214
356
|
}
|
|
357
|
+
return p;
|
|
215
358
|
}
|
|
216
359
|
return p;
|
|
217
360
|
} catch {
|
|
@@ -336,3 +479,21 @@ export function maybeRemapToolResult(name, result, workspaceRoot) {
|
|
|
336
479
|
return result;
|
|
337
480
|
}
|
|
338
481
|
}
|
|
482
|
+
|
|
483
|
+
export function maybeRemapToolArgs(name, args, workspaceRoot) {
|
|
484
|
+
try {
|
|
485
|
+
if (!name || !workspaceRoot) {
|
|
486
|
+
return args;
|
|
487
|
+
}
|
|
488
|
+
const enabled = envTruthy(process.env.CTXCE_BRIDGE_MAP_ARGS, true);
|
|
489
|
+
if (!enabled) {
|
|
490
|
+
return args;
|
|
491
|
+
}
|
|
492
|
+
if (args === null || args === undefined || typeof args !== "object") {
|
|
493
|
+
return args;
|
|
494
|
+
}
|
|
495
|
+
return applyPathMappingToArgs(args, workspaceRoot, "");
|
|
496
|
+
} catch {
|
|
497
|
+
return args;
|
|
498
|
+
}
|
|
499
|
+
}
|