@context-engine-bridge/context-engine-mcp-bridge 0.0.19 → 0.0.21
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/cli.js +22 -5
- package/src/mcpServer.js +38 -19
- package/src/oauthHandler.js +5 -1
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -23,6 +23,7 @@ export async function runCli() {
|
|
|
23
23
|
let indexerUrl = process.env.CTXCE_INDEXER_URL || "http://localhost:8003/mcp";
|
|
24
24
|
let memoryUrl = process.env.CTXCE_MEMORY_URL || null;
|
|
25
25
|
let port = Number.parseInt(process.env.CTXCE_HTTP_PORT || "30810", 10) || 30810;
|
|
26
|
+
let collection = null;
|
|
26
27
|
|
|
27
28
|
for (let i = 0; i < args.length; i += 1) {
|
|
28
29
|
const a = args[i];
|
|
@@ -57,13 +58,20 @@ export async function runCli() {
|
|
|
57
58
|
continue;
|
|
58
59
|
}
|
|
59
60
|
}
|
|
61
|
+
if (a === "--collection") {
|
|
62
|
+
if (i + 1 < args.length) {
|
|
63
|
+
collection = args[i + 1];
|
|
64
|
+
i += 1;
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
60
68
|
}
|
|
61
69
|
|
|
62
70
|
// eslint-disable-next-line no-console
|
|
63
71
|
console.error(
|
|
64
|
-
`[ctxce] Starting HTTP MCP bridge: workspace=${workspace}, port=${port}, indexerUrl=${indexerUrl}, memoryUrl=${memoryUrl || "disabled"}`,
|
|
72
|
+
`[ctxce] Starting HTTP MCP bridge: workspace=${workspace}, port=${port}, indexerUrl=${indexerUrl}, memoryUrl=${memoryUrl || "disabled"}, collection=${collection || "auto"}`,
|
|
65
73
|
);
|
|
66
|
-
await runHttpMcpServer({ workspace, indexerUrl, memoryUrl, port });
|
|
74
|
+
await runHttpMcpServer({ workspace, indexerUrl, memoryUrl, port, collection });
|
|
67
75
|
return;
|
|
68
76
|
}
|
|
69
77
|
|
|
@@ -72,10 +80,12 @@ export async function runCli() {
|
|
|
72
80
|
// Supported flags:
|
|
73
81
|
// --workspace / --path : workspace root (default: cwd)
|
|
74
82
|
// --indexer-url : override MCP indexer URL (default env CTXCE_INDEXER_URL or http://localhost:8003/mcp)
|
|
83
|
+
// --collection : collection name to use for MCP calls
|
|
75
84
|
const args = argv.slice(1);
|
|
76
85
|
let workspace = process.cwd();
|
|
77
86
|
let indexerUrl = process.env.CTXCE_INDEXER_URL || "http://localhost:8003/mcp";
|
|
78
87
|
let memoryUrl = process.env.CTXCE_MEMORY_URL || null;
|
|
88
|
+
let collection = null;
|
|
79
89
|
|
|
80
90
|
for (let i = 0; i < args.length; i += 1) {
|
|
81
91
|
const a = args[i];
|
|
@@ -100,13 +110,20 @@ export async function runCli() {
|
|
|
100
110
|
continue;
|
|
101
111
|
}
|
|
102
112
|
}
|
|
113
|
+
if (a === "--collection") {
|
|
114
|
+
if (i + 1 < args.length) {
|
|
115
|
+
collection = args[i + 1];
|
|
116
|
+
i += 1;
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
103
120
|
}
|
|
104
121
|
|
|
105
122
|
// eslint-disable-next-line no-console
|
|
106
123
|
console.error(
|
|
107
|
-
`[ctxce] Starting MCP bridge: workspace=${workspace}, indexerUrl=${indexerUrl}, memoryUrl=${memoryUrl || "disabled"}`,
|
|
124
|
+
`[ctxce] Starting MCP bridge: workspace=${workspace}, indexerUrl=${indexerUrl}, memoryUrl=${memoryUrl || "disabled"}, collection=${collection || "auto"}`,
|
|
108
125
|
);
|
|
109
|
-
await runMcpServer({ workspace, indexerUrl, memoryUrl });
|
|
126
|
+
await runMcpServer({ workspace, indexerUrl, memoryUrl, collection });
|
|
110
127
|
return;
|
|
111
128
|
}
|
|
112
129
|
|
|
@@ -117,7 +134,7 @@ export async function runCli() {
|
|
|
117
134
|
|
|
118
135
|
// eslint-disable-next-line no-console
|
|
119
136
|
console.error(
|
|
120
|
-
`Usage: ${binName} mcp-serve [--workspace <path>] [--indexer-url <url>] [--memory-url <url>] | ${binName} mcp-http-serve [--workspace <path>] [--indexer-url <url>] [--memory-url <url>] [--port <port>] | ${binName} auth <login|status|logout> [--backend-url <url>] [--token <token>] [--username <name> --password <pass>]`,
|
|
137
|
+
`Usage: ${binName} mcp-serve [--workspace <path>] [--indexer-url <url>] [--memory-url <url>] [--collection <name>] | ${binName} mcp-http-serve [--workspace <path>] [--indexer-url <url>] [--memory-url <url>] [--port <port>] [--collection <name>] | ${binName} auth <login|status|logout> [--backend-url <url>] [--token <token>] [--username <name> --password <pass>]`,
|
|
121
138
|
);
|
|
122
139
|
process.exit(1);
|
|
123
140
|
}
|
package/src/mcpServer.js
CHANGED
|
@@ -415,18 +415,8 @@ async function fetchBridgeCollectionState({
|
|
|
415
415
|
if (!resp.ok) {
|
|
416
416
|
if (resp.status === 401 || resp.status === 403) {
|
|
417
417
|
debugLog(
|
|
418
|
-
`[ctxce] /bridge/state responded ${resp.status};
|
|
418
|
+
`[ctxce] /bridge/state responded ${resp.status}; session may not be accepted by this endpoint.`,
|
|
419
419
|
);
|
|
420
|
-
if (backendHint) {
|
|
421
|
-
try {
|
|
422
|
-
const entry = loadAuthEntry(backendHint);
|
|
423
|
-
if (entry) {
|
|
424
|
-
saveAuthEntry(backendHint, { ...entry, expiresAt: 1 });
|
|
425
|
-
}
|
|
426
|
-
} catch {
|
|
427
|
-
// ignore failures
|
|
428
|
-
}
|
|
429
|
-
}
|
|
430
420
|
return null;
|
|
431
421
|
}
|
|
432
422
|
throw new Error(`bridge/state responded ${resp.status}`);
|
|
@@ -444,12 +434,15 @@ async function createBridgeServer(options) {
|
|
|
444
434
|
const workspace = options.workspace || process.cwd();
|
|
445
435
|
const indexerUrl = options.indexerUrl;
|
|
446
436
|
const memoryUrl = options.memoryUrl;
|
|
437
|
+
const explicitCollection = options.collection;
|
|
447
438
|
|
|
448
439
|
const config = loadConfig(workspace);
|
|
449
440
|
const defaultCollection =
|
|
450
|
-
|
|
451
|
-
?
|
|
452
|
-
:
|
|
441
|
+
explicitCollection && typeof explicitCollection === "string"
|
|
442
|
+
? explicitCollection
|
|
443
|
+
: config && typeof config.default_collection === "string"
|
|
444
|
+
? config.default_collection
|
|
445
|
+
: null;
|
|
453
446
|
const defaultMode =
|
|
454
447
|
config && typeof config.default_mode === "string" ? config.default_mode : null;
|
|
455
448
|
const defaultUnder =
|
|
@@ -503,8 +496,8 @@ async function createBridgeServer(options) {
|
|
|
503
496
|
expiresAt > 0 &&
|
|
504
497
|
expiresAt < Math.floor(Date.now() / 1000)
|
|
505
498
|
) {
|
|
506
|
-
debugLog("[ctxce] Stored auth session
|
|
507
|
-
return
|
|
499
|
+
debugLog("[ctxce] Stored auth session has local expiry in the past; attempting to use it anyway (server will validate).");
|
|
500
|
+
return entry.sessionId;
|
|
508
501
|
}
|
|
509
502
|
return entry.sessionId;
|
|
510
503
|
}
|
|
@@ -646,9 +639,34 @@ async function createBridgeServer(options) {
|
|
|
646
639
|
}
|
|
647
640
|
}
|
|
648
641
|
|
|
642
|
+
// Build requestInit with Authorization header for nginx auth_request passthrough.
|
|
643
|
+
// When the bridge connects to a remote endpoint (e.g. https://dev.example.com/indexer/mcp),
|
|
644
|
+
// nginx's auth_request subrequest checks the Authorization header. Without it, the
|
|
645
|
+
// connection is rejected with 401.
|
|
646
|
+
const transportOpts = {};
|
|
647
|
+
if (sessionId && !sessionId.startsWith("ctxce-")) {
|
|
648
|
+
transportOpts.requestInit = {
|
|
649
|
+
headers: {
|
|
650
|
+
Authorization: `Bearer ${sessionId}`,
|
|
651
|
+
},
|
|
652
|
+
};
|
|
653
|
+
debugLog(`[ctxce] Transport auth: injecting Authorization header (session ${sessionId.slice(0, 8)}...)`);
|
|
654
|
+
} else {
|
|
655
|
+
// Check for API key in environment as fallback
|
|
656
|
+
const envApiKey = (process.env.CTXCE_API_KEY || process.env.CTXCE_AUTH_TOKEN || "").trim();
|
|
657
|
+
if (envApiKey) {
|
|
658
|
+
transportOpts.requestInit = {
|
|
659
|
+
headers: {
|
|
660
|
+
Authorization: `Bearer ${envApiKey}`,
|
|
661
|
+
},
|
|
662
|
+
};
|
|
663
|
+
debugLog("[ctxce] Transport auth: injecting Authorization header (env API key/token)");
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
|
|
649
667
|
let nextIndexerClient = null;
|
|
650
668
|
try {
|
|
651
|
-
const indexerTransport = new StreamableHTTPClientTransport(indexerUrl);
|
|
669
|
+
const indexerTransport = new StreamableHTTPClientTransport(indexerUrl, transportOpts);
|
|
652
670
|
const client = new Client(
|
|
653
671
|
{
|
|
654
672
|
name: "ctx-context-engine-bridge-http-client",
|
|
@@ -672,7 +690,7 @@ async function createBridgeServer(options) {
|
|
|
672
690
|
let nextMemoryClient = null;
|
|
673
691
|
if (memoryUrl) {
|
|
674
692
|
try {
|
|
675
|
-
const memoryTransport = new StreamableHTTPClientTransport(memoryUrl);
|
|
693
|
+
const memoryTransport = new StreamableHTTPClientTransport(memoryUrl, transportOpts);
|
|
676
694
|
const client = new Client(
|
|
677
695
|
{
|
|
678
696
|
name: "ctx-context-engine-bridge-memory-client",
|
|
@@ -971,7 +989,8 @@ export async function runHttpMcpServer(options) {
|
|
|
971
989
|
|
|
972
990
|
// Pre-warm: validate upstream connectivity (best-effort)
|
|
973
991
|
try {
|
|
974
|
-
await createBridgeServer(options);
|
|
992
|
+
const warmupServer = await createBridgeServer(options);
|
|
993
|
+
await warmupServer.close();
|
|
975
994
|
debugLog("[ctxce] HTTP bridge validated upstream connectivity");
|
|
976
995
|
} catch (err) {
|
|
977
996
|
debugLog("[ctxce] HTTP bridge warmup failed (non-fatal): " + String(err));
|
package/src/oauthHandler.js
CHANGED
|
@@ -277,7 +277,11 @@ export function getLoginPage(redirectUri, clientId, state, codeChallenge, codeCh
|
|
|
277
277
|
throw new Error('No redirect URL');
|
|
278
278
|
}
|
|
279
279
|
} catch (err) {
|
|
280
|
-
result.
|
|
280
|
+
result.textContent = '';
|
|
281
|
+
var p = document.createElement('p');
|
|
282
|
+
p.className = 'error';
|
|
283
|
+
p.textContent = err.message;
|
|
284
|
+
result.appendChild(p);
|
|
281
285
|
}
|
|
282
286
|
});
|
|
283
287
|
</script>
|