@context-engine-bridge/context-engine-mcp-bridge 0.0.1 → 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/docs/debugging.md +20 -0
- package/package.json +2 -2
- package/publish.sh +34 -0
- package/src/cli.js +52 -2
- package/src/mcpServer.js +190 -54
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"mcpServers": {
|
|
3
|
+
"context-engine": {
|
|
4
|
+
"command": "node",
|
|
5
|
+
"args": [
|
|
6
|
+
"C:/Users/Admin/Documents/GitHub/Context-Engine/ctx-mcp-bridge/bin/ctxce.js",
|
|
7
|
+
"mcp-serve",
|
|
8
|
+
"--indexer-url",
|
|
9
|
+
"http://192.168.100.249:30806/mcp",
|
|
10
|
+
"--memory-url",
|
|
11
|
+
"http://192.168.100.249:30804/mcp",
|
|
12
|
+
"--workspace",
|
|
13
|
+
"C:/Users/Admin/Documents/GitHub/Pirate Survivors"
|
|
14
|
+
],
|
|
15
|
+
"env": {
|
|
16
|
+
"CTXCE_DEBUG_LOG": "C:/Users/Admin/ctxce-mcp.log"
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
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/publish.sh
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
# Simple helper to login (if needed) and publish the package.
|
|
5
|
+
# Usage:
|
|
6
|
+
# ./publish.sh # publishes current version
|
|
7
|
+
# ./publish.sh 0.0.2 # bumps version to 0.0.2 then publishes
|
|
8
|
+
|
|
9
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
10
|
+
cd "$SCRIPT_DIR"
|
|
11
|
+
|
|
12
|
+
PACKAGE_NAME="@context-engine-bridge/context-engine-mcp-bridge"
|
|
13
|
+
|
|
14
|
+
echo "[publish] Verifying npm authentication..."
|
|
15
|
+
if ! npm whoami >/dev/null 2>&1; then
|
|
16
|
+
echo "[publish] Not logged in; running npm login"
|
|
17
|
+
npm login
|
|
18
|
+
else
|
|
19
|
+
echo "[publish] Already authenticated as $(npm whoami)"
|
|
20
|
+
fi
|
|
21
|
+
|
|
22
|
+
if [[ $# -gt 0 ]]; then
|
|
23
|
+
VERSION="$1"
|
|
24
|
+
echo "[publish] Bumping version to $VERSION"
|
|
25
|
+
npm version "$VERSION" --no-git-tag-version
|
|
26
|
+
fi
|
|
27
|
+
|
|
28
|
+
echo "[publish] Packing $PACKAGE_NAME for verification..."
|
|
29
|
+
npm pack >/dev/null
|
|
30
|
+
|
|
31
|
+
echo "[publish] Publishing $PACKAGE_NAME..."
|
|
32
|
+
npm publish --access public
|
|
33
|
+
|
|
34
|
+
echo "[publish] Done!"
|
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
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
function debugLog(message) {
|
|
2
|
+
try {
|
|
3
|
+
const text = typeof message === "string" ? message : String(message);
|
|
4
|
+
console.error(text);
|
|
5
|
+
const dest = process.env.CTXCE_DEBUG_LOG;
|
|
6
|
+
if (dest) {
|
|
7
|
+
fs.appendFileSync(dest, `${new Date().toISOString()} ${text}\n`, "utf8");
|
|
8
|
+
}
|
|
9
|
+
} catch {
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
1
13
|
async function sendSessionDefaults(client, payload, label) {
|
|
2
14
|
if (!client) {
|
|
3
15
|
return;
|
|
@@ -36,15 +48,52 @@ async function listMemoryTools(client) {
|
|
|
36
48
|
return [];
|
|
37
49
|
}
|
|
38
50
|
try {
|
|
39
|
-
const remote = await
|
|
51
|
+
const remote = await withTimeout(
|
|
52
|
+
client.listTools(),
|
|
53
|
+
5000,
|
|
54
|
+
"memory tools/list",
|
|
55
|
+
);
|
|
40
56
|
return Array.isArray(remote?.tools) ? remote.tools.slice() : [];
|
|
41
57
|
} catch (err) {
|
|
42
|
-
|
|
43
|
-
console.error("[ctxce] Error calling memory tools/list:", err);
|
|
58
|
+
debugLog("[ctxce] Error calling memory tools/list: " + String(err));
|
|
44
59
|
return [];
|
|
45
60
|
}
|
|
46
61
|
}
|
|
47
62
|
|
|
63
|
+
function withTimeout(promise, ms, label) {
|
|
64
|
+
return new Promise((resolve, reject) => {
|
|
65
|
+
let settled = false;
|
|
66
|
+
const timer = setTimeout(() => {
|
|
67
|
+
if (settled) {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
settled = true;
|
|
71
|
+
const errorMessage =
|
|
72
|
+
label != null
|
|
73
|
+
? `[ctxce] Timeout after ${ms}ms in ${label}`
|
|
74
|
+
: `[ctxce] Timeout after ${ms}ms`;
|
|
75
|
+
reject(new Error(errorMessage));
|
|
76
|
+
}, ms);
|
|
77
|
+
promise
|
|
78
|
+
.then((value) => {
|
|
79
|
+
if (settled) {
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
settled = true;
|
|
83
|
+
clearTimeout(timer);
|
|
84
|
+
resolve(value);
|
|
85
|
+
})
|
|
86
|
+
.catch((err) => {
|
|
87
|
+
if (settled) {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
settled = true;
|
|
91
|
+
clearTimeout(timer);
|
|
92
|
+
reject(err);
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
|
|
48
97
|
function selectClientForTool(name, indexerClient, memoryClient) {
|
|
49
98
|
if (!name) {
|
|
50
99
|
return indexerClient;
|
|
@@ -63,13 +112,15 @@ import process from "node:process";
|
|
|
63
112
|
import fs from "node:fs";
|
|
64
113
|
import path from "node:path";
|
|
65
114
|
import { execSync } from "node:child_process";
|
|
115
|
+
import { createServer } from "node:http";
|
|
66
116
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
67
117
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
118
|
+
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
68
119
|
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
69
120
|
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
70
121
|
import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
71
122
|
|
|
72
|
-
|
|
123
|
+
async function createBridgeServer(options) {
|
|
73
124
|
const workspace = options.workspace || process.cwd();
|
|
74
125
|
const indexerUrl = options.indexerUrl;
|
|
75
126
|
const memoryUrl = options.memoryUrl;
|
|
@@ -84,8 +135,7 @@ export async function runMcpServer(options) {
|
|
|
84
135
|
const defaultUnder =
|
|
85
136
|
config && typeof config.default_under === "string" ? config.default_under : null;
|
|
86
137
|
|
|
87
|
-
|
|
88
|
-
console.error(
|
|
138
|
+
debugLog(
|
|
89
139
|
`[ctxce] MCP low-level stdio bridge starting: workspace=${workspace}, indexerUrl=${indexerUrl}`,
|
|
90
140
|
);
|
|
91
141
|
|
|
@@ -115,9 +165,7 @@ export async function runMcpServer(options) {
|
|
|
115
165
|
try {
|
|
116
166
|
await indexerClient.connect(indexerTransport);
|
|
117
167
|
} catch (err) {
|
|
118
|
-
|
|
119
|
-
console.error("[ctxce] Failed to connect MCP HTTP client to indexer:", err);
|
|
120
|
-
throw err;
|
|
168
|
+
debugLog("[ctxce] Failed to connect MCP HTTP client to indexer: " + String(err));
|
|
121
169
|
}
|
|
122
170
|
|
|
123
171
|
let memoryClient = null;
|
|
@@ -138,11 +186,9 @@ export async function runMcpServer(options) {
|
|
|
138
186
|
},
|
|
139
187
|
);
|
|
140
188
|
await memoryClient.connect(memoryTransport);
|
|
141
|
-
|
|
142
|
-
console.error("[ctxce] Connected memory MCP client:", memoryUrl);
|
|
189
|
+
debugLog(`[ctxce] Connected memory MCP client: ${memoryUrl}`);
|
|
143
190
|
} catch (err) {
|
|
144
|
-
|
|
145
|
-
console.error("[ctxce] Failed to connect memory MCP client:", err);
|
|
191
|
+
debugLog("[ctxce] Failed to connect memory MCP client: " + String(err));
|
|
146
192
|
memoryClient = null;
|
|
147
193
|
}
|
|
148
194
|
}
|
|
@@ -186,45 +232,47 @@ export async function runMcpServer(options) {
|
|
|
186
232
|
},
|
|
187
233
|
);
|
|
188
234
|
|
|
189
|
-
// tools/list → fetch tools from remote indexer
|
|
235
|
+
// tools/list → fetch tools from remote indexer
|
|
190
236
|
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
191
237
|
let remote;
|
|
192
238
|
try {
|
|
193
|
-
|
|
239
|
+
debugLog("[ctxce] tools/list: fetching tools from indexer");
|
|
240
|
+
remote = await withTimeout(
|
|
241
|
+
indexerClient.listTools(),
|
|
242
|
+
10000,
|
|
243
|
+
"indexer tools/list",
|
|
244
|
+
);
|
|
194
245
|
} catch (err) {
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
246
|
+
debugLog("[ctxce] Error calling remote tools/list: " + String(err));
|
|
247
|
+
const memoryToolsFallback = await listMemoryTools(memoryClient);
|
|
248
|
+
const toolsFallback = dedupeTools([...memoryToolsFallback]);
|
|
249
|
+
return { tools: toolsFallback };
|
|
198
250
|
}
|
|
199
251
|
|
|
200
|
-
|
|
201
|
-
|
|
252
|
+
try {
|
|
253
|
+
const toolNames =
|
|
254
|
+
remote && Array.isArray(remote.tools)
|
|
255
|
+
? remote.tools.map((t) => (t && typeof t.name === "string" ? t.name : "<unnamed>"))
|
|
256
|
+
: [];
|
|
257
|
+
debugLog("[ctxce] tools/list remote result tools: " + JSON.stringify(toolNames));
|
|
258
|
+
} catch (err) {
|
|
259
|
+
debugLog("[ctxce] tools/list remote result: <unserializable> " + String(err));
|
|
260
|
+
}
|
|
202
261
|
|
|
203
262
|
const indexerTools = Array.isArray(remote?.tools) ? remote.tools.slice() : [];
|
|
204
263
|
const memoryTools = await listMemoryTools(memoryClient);
|
|
205
|
-
const tools = dedupeTools([...indexerTools, ...memoryTools
|
|
264
|
+
const tools = dedupeTools([...indexerTools, ...memoryTools]);
|
|
265
|
+
debugLog(`[ctxce] tools/list: returning ${tools.length} tools`);
|
|
206
266
|
return { tools };
|
|
207
267
|
});
|
|
208
268
|
|
|
209
|
-
// tools/call →
|
|
269
|
+
// tools/call → proxied to indexer or memory server
|
|
210
270
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
211
271
|
const params = request.params || {};
|
|
212
272
|
const name = params.name;
|
|
213
273
|
let args = params.arguments;
|
|
214
274
|
|
|
215
|
-
|
|
216
|
-
const branch = detectGitBranch(workspace);
|
|
217
|
-
const text = args && typeof args.text === "string" ? args.text : "pong";
|
|
218
|
-
const suffix = branch ? ` (branch=${branch})` : "";
|
|
219
|
-
return {
|
|
220
|
-
content: [
|
|
221
|
-
{
|
|
222
|
-
type: "text",
|
|
223
|
-
text: `${text}${suffix}`,
|
|
224
|
-
},
|
|
225
|
-
],
|
|
226
|
-
};
|
|
227
|
-
}
|
|
275
|
+
debugLog(`[ctxce] tools/call: ${name || "<no-name>"}`);
|
|
228
276
|
|
|
229
277
|
// Attach session id so the target server can apply per-session defaults.
|
|
230
278
|
if (sessionId && (args === undefined || args === null || typeof args === "object")) {
|
|
@@ -241,8 +289,7 @@ export async function runMcpServer(options) {
|
|
|
241
289
|
try {
|
|
242
290
|
await memoryClient.callTool({ name, arguments: args });
|
|
243
291
|
} catch (err) {
|
|
244
|
-
|
|
245
|
-
console.error("[ctxce] Memory set_session_defaults failed:", err);
|
|
292
|
+
debugLog("[ctxce] Memory set_session_defaults failed: " + String(err));
|
|
246
293
|
}
|
|
247
294
|
}
|
|
248
295
|
return indexerResult;
|
|
@@ -260,10 +307,116 @@ export async function runMcpServer(options) {
|
|
|
260
307
|
return result;
|
|
261
308
|
});
|
|
262
309
|
|
|
310
|
+
return server;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
export async function runMcpServer(options) {
|
|
314
|
+
const server = await createBridgeServer(options);
|
|
263
315
|
const transport = new StdioServerTransport();
|
|
264
316
|
await server.connect(transport);
|
|
265
317
|
}
|
|
266
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
|
+
|
|
267
420
|
function loadConfig(startDir) {
|
|
268
421
|
try {
|
|
269
422
|
let dir = startDir;
|
|
@@ -295,23 +448,6 @@ function loadConfig(startDir) {
|
|
|
295
448
|
return null;
|
|
296
449
|
}
|
|
297
450
|
|
|
298
|
-
function buildPingTool() {
|
|
299
|
-
return {
|
|
300
|
-
name: "ping",
|
|
301
|
-
description: "Basic ping tool exposed by the ctx bridge",
|
|
302
|
-
inputSchema: {
|
|
303
|
-
type: "object",
|
|
304
|
-
properties: {
|
|
305
|
-
text: {
|
|
306
|
-
type: "string",
|
|
307
|
-
description: "Optional text to echo back.",
|
|
308
|
-
},
|
|
309
|
-
},
|
|
310
|
-
required: [],
|
|
311
|
-
},
|
|
312
|
-
};
|
|
313
|
-
}
|
|
314
|
-
|
|
315
451
|
function detectGitBranch(workspace) {
|
|
316
452
|
try {
|
|
317
453
|
const out = execSync("git rev-parse --abbrev-ref HEAD", {
|