@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 CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@context-engine-bridge/context-engine-mcp-bridge",
3
- "version": "0.0.2",
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
- export async function runMcpServer(options) {
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;