@context-engine-bridge/context-engine-mcp-bridge 0.0.2 → 0.0.3
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 +2 -2
- package/src/cli.js +52 -2
- package/src/mcpServer.js +109 -2
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@context-engine-bridge/context-engine-mcp-bridge",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"description": "Context Engine MCP bridge (stdio proxy combining indexer + memory servers)",
|
|
3
|
+
"version": "0.0.3",
|
|
4
|
+
"description": "Context Engine MCP bridge (http/stdio proxy combining indexer + memory servers)",
|
|
5
5
|
"bin": {
|
|
6
6
|
"ctxce": "bin/ctxce.js",
|
|
7
7
|
"ctxce-bridge": "bin/ctxce.js"
|
package/src/cli.js
CHANGED
|
@@ -3,12 +3,62 @@
|
|
|
3
3
|
import process from "node:process";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
|
-
import { runMcpServer } from "./mcpServer.js";
|
|
6
|
+
import { runMcpServer, runHttpMcpServer } from "./mcpServer.js";
|
|
7
7
|
|
|
8
8
|
export async function runCli() {
|
|
9
9
|
const argv = process.argv.slice(2);
|
|
10
10
|
const cmd = argv[0];
|
|
11
11
|
|
|
12
|
+
if (cmd === "mcp-http-serve") {
|
|
13
|
+
const args = argv.slice(1);
|
|
14
|
+
let workspace = process.cwd();
|
|
15
|
+
let indexerUrl = process.env.CTXCE_INDEXER_URL || "http://localhost:8003/mcp";
|
|
16
|
+
let memoryUrl = process.env.CTXCE_MEMORY_URL || null;
|
|
17
|
+
let port = Number.parseInt(process.env.CTXCE_HTTP_PORT || "30810", 10) || 30810;
|
|
18
|
+
|
|
19
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
20
|
+
const a = args[i];
|
|
21
|
+
if (a === "--workspace" || a === "--path") {
|
|
22
|
+
if (i + 1 < args.length) {
|
|
23
|
+
workspace = args[i + 1];
|
|
24
|
+
i += 1;
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
if (a === "--indexer-url") {
|
|
29
|
+
if (i + 1 < args.length) {
|
|
30
|
+
indexerUrl = args[i + 1];
|
|
31
|
+
i += 1;
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
if (a === "--memory-url") {
|
|
36
|
+
if (i + 1 < args.length) {
|
|
37
|
+
memoryUrl = args[i + 1];
|
|
38
|
+
i += 1;
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
if (a === "--port") {
|
|
43
|
+
if (i + 1 < args.length) {
|
|
44
|
+
const parsed = Number.parseInt(args[i + 1], 10);
|
|
45
|
+
if (!Number.isNaN(parsed) && parsed > 0) {
|
|
46
|
+
port = parsed;
|
|
47
|
+
}
|
|
48
|
+
i += 1;
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// eslint-disable-next-line no-console
|
|
55
|
+
console.error(
|
|
56
|
+
`[ctxce] Starting HTTP MCP bridge: workspace=${workspace}, port=${port}, indexerUrl=${indexerUrl}, memoryUrl=${memoryUrl || "disabled"}`,
|
|
57
|
+
);
|
|
58
|
+
await runHttpMcpServer({ workspace, indexerUrl, memoryUrl, port });
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
|
|
12
62
|
if (cmd === "mcp-serve") {
|
|
13
63
|
// Minimal flag parsing for PoC: allow passing workspace/root and indexer URL.
|
|
14
64
|
// Supported flags:
|
|
@@ -59,7 +109,7 @@ export async function runCli() {
|
|
|
59
109
|
|
|
60
110
|
// eslint-disable-next-line no-console
|
|
61
111
|
console.error(
|
|
62
|
-
`Usage: ${binName} mcp-serve [--workspace <path>] [--indexer-url <url>] [--memory-url <url>]`,
|
|
112
|
+
`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>]`,
|
|
63
113
|
);
|
|
64
114
|
process.exit(1);
|
|
65
115
|
}
|
package/src/mcpServer.js
CHANGED
|
@@ -112,13 +112,15 @@ import process from "node:process";
|
|
|
112
112
|
import fs from "node:fs";
|
|
113
113
|
import path from "node:path";
|
|
114
114
|
import { execSync } from "node:child_process";
|
|
115
|
+
import { createServer } from "node:http";
|
|
115
116
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
116
117
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
118
|
+
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
117
119
|
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
118
120
|
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
119
121
|
import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
120
122
|
|
|
121
|
-
|
|
123
|
+
async function createBridgeServer(options) {
|
|
122
124
|
const workspace = options.workspace || process.cwd();
|
|
123
125
|
const indexerUrl = options.indexerUrl;
|
|
124
126
|
const memoryUrl = options.memoryUrl;
|
|
@@ -164,7 +166,6 @@ export async function runMcpServer(options) {
|
|
|
164
166
|
await indexerClient.connect(indexerTransport);
|
|
165
167
|
} catch (err) {
|
|
166
168
|
debugLog("[ctxce] Failed to connect MCP HTTP client to indexer: " + String(err));
|
|
167
|
-
throw err;
|
|
168
169
|
}
|
|
169
170
|
|
|
170
171
|
let memoryClient = null;
|
|
@@ -306,10 +307,116 @@ export async function runMcpServer(options) {
|
|
|
306
307
|
return result;
|
|
307
308
|
});
|
|
308
309
|
|
|
310
|
+
return server;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
export async function runMcpServer(options) {
|
|
314
|
+
const server = await createBridgeServer(options);
|
|
309
315
|
const transport = new StdioServerTransport();
|
|
310
316
|
await server.connect(transport);
|
|
311
317
|
}
|
|
312
318
|
|
|
319
|
+
export async function runHttpMcpServer(options) {
|
|
320
|
+
const server = await createBridgeServer(options);
|
|
321
|
+
const port =
|
|
322
|
+
typeof options.port === "number"
|
|
323
|
+
? options.port
|
|
324
|
+
: Number.parseInt(process.env.CTXCE_HTTP_PORT || "30810", 10) || 30810;
|
|
325
|
+
|
|
326
|
+
const transport = new StreamableHTTPServerTransport({
|
|
327
|
+
sessionIdGenerator: undefined,
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
await server.connect(transport);
|
|
331
|
+
|
|
332
|
+
const httpServer = createServer((req, res) => {
|
|
333
|
+
try {
|
|
334
|
+
if (!req.url || !req.url.startsWith("/mcp")) {
|
|
335
|
+
res.statusCode = 404;
|
|
336
|
+
res.setHeader("Content-Type", "application/json");
|
|
337
|
+
res.end(
|
|
338
|
+
JSON.stringify({
|
|
339
|
+
jsonrpc: "2.0",
|
|
340
|
+
error: { code: -32000, message: "Not found" },
|
|
341
|
+
id: null,
|
|
342
|
+
}),
|
|
343
|
+
);
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
if (req.method !== "POST") {
|
|
348
|
+
res.statusCode = 405;
|
|
349
|
+
res.setHeader("Content-Type", "application/json");
|
|
350
|
+
res.end(
|
|
351
|
+
JSON.stringify({
|
|
352
|
+
jsonrpc: "2.0",
|
|
353
|
+
error: { code: -32000, message: "Method not allowed" },
|
|
354
|
+
id: null,
|
|
355
|
+
}),
|
|
356
|
+
);
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
let body = "";
|
|
361
|
+
req.on("data", (chunk) => {
|
|
362
|
+
body += chunk;
|
|
363
|
+
});
|
|
364
|
+
req.on("end", async () => {
|
|
365
|
+
let parsed;
|
|
366
|
+
try {
|
|
367
|
+
parsed = body ? JSON.parse(body) : {};
|
|
368
|
+
} catch (err) {
|
|
369
|
+
debugLog("[ctxce] Failed to parse HTTP MCP request body: " + String(err));
|
|
370
|
+
res.statusCode = 400;
|
|
371
|
+
res.setHeader("Content-Type", "application/json");
|
|
372
|
+
res.end(
|
|
373
|
+
JSON.stringify({
|
|
374
|
+
jsonrpc: "2.0",
|
|
375
|
+
error: { code: -32700, message: "Invalid JSON" },
|
|
376
|
+
id: null,
|
|
377
|
+
}),
|
|
378
|
+
);
|
|
379
|
+
return;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
try {
|
|
383
|
+
await transport.handleRequest(req, res, parsed);
|
|
384
|
+
} catch (err) {
|
|
385
|
+
debugLog("[ctxce] Error handling HTTP MCP request: " + String(err));
|
|
386
|
+
if (!res.headersSent) {
|
|
387
|
+
res.statusCode = 500;
|
|
388
|
+
res.setHeader("Content-Type", "application/json");
|
|
389
|
+
res.end(
|
|
390
|
+
JSON.stringify({
|
|
391
|
+
jsonrpc: "2.0",
|
|
392
|
+
error: { code: -32603, message: "Internal server error" },
|
|
393
|
+
id: null,
|
|
394
|
+
}),
|
|
395
|
+
);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
});
|
|
399
|
+
} catch (err) {
|
|
400
|
+
debugLog("[ctxce] Unexpected error in HTTP MCP server: " + String(err));
|
|
401
|
+
if (!res.headersSent) {
|
|
402
|
+
res.statusCode = 500;
|
|
403
|
+
res.setHeader("Content-Type", "application/json");
|
|
404
|
+
res.end(
|
|
405
|
+
JSON.stringify({
|
|
406
|
+
jsonrpc: "2.0",
|
|
407
|
+
error: { code: -32603, message: "Internal server error" },
|
|
408
|
+
id: null,
|
|
409
|
+
}),
|
|
410
|
+
);
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
});
|
|
414
|
+
|
|
415
|
+
httpServer.listen(port, () => {
|
|
416
|
+
debugLog(`[ctxce] HTTP MCP bridge listening on port ${port}`);
|
|
417
|
+
});
|
|
418
|
+
}
|
|
419
|
+
|
|
313
420
|
function loadConfig(startDir) {
|
|
314
421
|
try {
|
|
315
422
|
let dir = startDir;
|