|
1 | 1 | #!/usr/bin/env node |
2 | | -// SPDX-License-Identifier: PMPL-1.0-or-later |
| 2 | +// Tool dispatch |
| 3 | +// =================================================================== |
| 4 | + |
| 5 | +/** |
| 6 | + * Dispatch a validated tool call to the appropriate handler. |
| 7 | + * @param {string} toolName |
| 8 | + * @param {Record<string, unknown>} args |
| 9 | + * @returns {Promise<object>} |
| 10 | + */ |
| 11 | +async function dispatchTool(toolName, args) { |
| 12 | +======= |
| 13 | +// =================================================================== |
| 14 | +// Group management |
| 15 | +// =================================================================== |
| 16 | + |
| 17 | +/** |
| 18 | + * Fetch the list of available groups. |
| 19 | + * @returns {Promise<Array<{id: string, name: string, description: string}>>} |
| 20 | + */ |
| 21 | +async function fetchGroups() { |
| 22 | + // For now, return a static list of groups. This can be extended to fetch |
| 23 | + // groups from a configuration file or database. |
| 24 | + return [ |
| 25 | + { id: "database", name: "Database", description: "Database-related tools" }, |
| 26 | + { id: "cloud", name: "Cloud", description: "Cloud-related tools" }, |
| 27 | + { id: "git", name: "Git", description: "Git-related tools" }, |
| 28 | + { id: "comms", name: "Comms", description: "Communication-related tools" }, |
| 29 | + ]; |
| 30 | +} |
| 31 | + |
| 32 | +/** |
| 33 | + * Dispatch a validated tool call to the appropriate handler within a group. |
| 34 | + * @param {string} groupId |
| 35 | + * @param {string} toolName |
| 36 | + * @param {Record<string, unknown>} args |
| 37 | + * @returns {Promise<object>} |
| 38 | + */ |
| 39 | +async function dispatchGroupTool(groupId, toolName, args) { |
| 40 | + // Map group IDs to their respective cartridges |
| 41 | + const groupToCartridge = { |
| 42 | + database: "database-mcp", |
| 43 | + cloud: "cloud-mcp", |
| 44 | + git: "git-mcp", |
| 45 | + comms: "comms-mcp", |
| 46 | + }; |
| 47 | + |
| 48 | + const cartridgeName = groupToCartridge[groupId]; |
| 49 | + if (!cartridgeName) { |
| 50 | + return null; |
| 51 | + } |
| 52 | + |
| 53 | + // Dispatch the tool call to the appropriate cartridge |
| 54 | + return invokeCartridge(cartridgeName, args); |
| 55 | +} |
| 56 | + |
| 57 | +// =================================================================== |
| 58 | +// Tool dispatch |
| 59 | +// =================================================================== |
| 60 | + |
| 61 | +/** |
| 62 | + * Dispatch a validated tool call to the appropriate handler. |
| 63 | + * @param {string} toolName |
| 64 | + * @param {Record<string, unknown>} args |
| 65 | + * @returns {Promise<object>} |
| 66 | + */ |
| 67 | +async function dispatchTool(toolName, args) {SPDX-License-Identifier: PMPL-1.0-or-later |
3 | 68 | // Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) <j.d.a.jewell@open.ac.uk> |
4 | 69 | // |
5 | 70 | // BoJ Server — MCP stdio transport bridge |
@@ -39,7 +104,7 @@ import { info, warn, error as logError } from "./lib/logger.js"; |
39 | 104 |
|
40 | 105 | const BOJ_BASE = process.env.BOJ_URL || "http://localhost:7700"; |
41 | 106 | const SERVER_NAME = "boj-server"; |
42 | | -const SERVER_VERSION = "0.3.1"; |
| 107 | +const SERVER_VERSION = "0.4.0"; |
43 | 108 |
|
44 | 109 | // =================================================================== |
45 | 110 | // JSON-RPC stdio transport |
@@ -367,6 +432,39 @@ async function handleMessage(line) { |
367 | 432 | break; |
368 | 433 | } |
369 | 434 |
|
| 435 | + case "groups/list": { |
| 436 | + const groups = await fetchGroups(); |
| 437 | + sendResult(id, { groups }); |
| 438 | + break; |
| 439 | + } |
| 440 | + |
| 441 | + case "groups/call": { |
| 442 | + const groupId = params?.groupId; |
| 443 | + const toolName = params?.name; |
| 444 | + const args = params?.arguments || {}; |
| 445 | + |
| 446 | + if (!groupId) { |
| 447 | + sendError(id, -32602, "Group ID is required"); |
| 448 | + break; |
| 449 | + } |
| 450 | + |
| 451 | + const rejection = hardeningGate(toolName, args); |
| 452 | + if (rejection) { |
| 453 | + sendError(id, rejection.code, rejection.message); |
| 454 | + break; |
| 455 | + } |
| 456 | + |
| 457 | + const result = await dispatchGroupTool(groupId, toolName, args); |
| 458 | + if (result === null) { |
| 459 | + sendError(id, -32601, "Unknown tool or group"); |
| 460 | + } else { |
| 461 | + sendResult(id, { |
| 462 | + content: [{ type: "text", text: JSON.stringify(result, null, 2) }], |
| 463 | + }); |
| 464 | + } |
| 465 | + break; |
| 466 | + } |
| 467 | + |
370 | 468 | case "ping": |
371 | 469 | sendResult(id, {}); |
372 | 470 | break; |
|
0 commit comments