metheus-governance-mcp-cli 0.2.4 → 0.2.5
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/cli.mjs +107 -14
- package/package.json +1 -1
package/cli.mjs
CHANGED
|
@@ -26,12 +26,12 @@ function printUsage() {
|
|
|
26
26
|
"Metheus Governance MCP CLI",
|
|
27
27
|
"",
|
|
28
28
|
"Usage:",
|
|
29
|
-
" metheus-governance-mcp [--project-id <uuid>] [--ctxpack-key <key>] [--base-url <url>] [--flow <auto|device|callback|manual>]",
|
|
29
|
+
" metheus-governance-mcp [--project-id <uuid>] [--ctxpack-key <key>] [--base-url <url>] [--workspace-dir <path>] [--flow <auto|device|callback|manual>]",
|
|
30
30
|
" metheus-governance-mcp init [--project-id <uuid>] [--ctxpack-key <key>] [--base-url <url>] [--flow <auto|device|callback|manual>]",
|
|
31
|
-
" metheus-governance-mcp setup [--project-id <uuid>] [--ctxpack-key <key>] [--base-url <url>] [--name <server_name>]",
|
|
31
|
+
" metheus-governance-mcp setup [--project-id <uuid>] [--ctxpack-key <key>] [--base-url <url>] [--workspace-dir <path>] [--name <server_name>]",
|
|
32
32
|
" metheus-governance-mcp doctor [--project-id <uuid>] [--ctxpack-key <key>] [--base-url <url>] [--timeout-seconds <n>]",
|
|
33
|
-
" metheus-governance-mcp proxy [--project-id <uuid>] [--ctxpack-key <key>] [--base-url <url>] [--include-drafts <true|false>] [--timeout-seconds <n>]",
|
|
34
|
-
" metheus-governance-mcp ctxpack pull [--project-id <uuid>] [--base-url <url>] [--timeout-seconds <n>]",
|
|
33
|
+
" metheus-governance-mcp proxy [--project-id <uuid>] [--ctxpack-key <key>] [--base-url <url>] [--workspace-dir <path>] [--include-drafts <true|false>] [--timeout-seconds <n>]",
|
|
34
|
+
" metheus-governance-mcp ctxpack pull [--project-id <uuid>] [--base-url <url>] [--workspace-dir <path>] [--timeout-seconds <n>]",
|
|
35
35
|
" metheus-governance-mcp auth status",
|
|
36
36
|
" metheus-governance-mcp auth login [--base-url <url>] [--flow <auto|device|callback|manual>] [--keycloak-url <url>] [--realm <name>] [--client-id <id>] [--open-browser <true|false>] [--callback-port <n>] [--timeout-seconds <n>] [--manual <true|false>]",
|
|
37
37
|
" metheus-governance-mcp auth set --token <jwt> [--refresh-token <token>] [--base-url <url>]",
|
|
@@ -101,7 +101,15 @@ function authStoreFilePath() {
|
|
|
101
101
|
return path.join(home, AUTH_STORE_RELATIVE_PATH);
|
|
102
102
|
}
|
|
103
103
|
|
|
104
|
-
function
|
|
104
|
+
function resolveWorkspaceDir(rawPath) {
|
|
105
|
+
const input = String(rawPath || "").trim();
|
|
106
|
+
if (input) {
|
|
107
|
+
return path.resolve(input);
|
|
108
|
+
}
|
|
109
|
+
return path.resolve(process.cwd());
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function homeCtxpackCacheRootDir() {
|
|
105
113
|
const home = String(process.env.USERPROFILE || process.env.HOME || "").trim();
|
|
106
114
|
if (!home) {
|
|
107
115
|
return path.resolve(process.cwd(), CTXPACK_CACHE_RELATIVE_DIR);
|
|
@@ -109,6 +117,14 @@ function ctxpackCacheRootDir() {
|
|
|
109
117
|
return path.join(home, CTXPACK_CACHE_RELATIVE_DIR);
|
|
110
118
|
}
|
|
111
119
|
|
|
120
|
+
function ctxpackCacheRootDir(workspaceDir) {
|
|
121
|
+
const root = resolveWorkspaceDir(workspaceDir);
|
|
122
|
+
if (!root) {
|
|
123
|
+
return homeCtxpackCacheRootDir();
|
|
124
|
+
}
|
|
125
|
+
return path.join(root, CTXPACK_CACHE_RELATIVE_DIR);
|
|
126
|
+
}
|
|
127
|
+
|
|
112
128
|
function ensureParentDir(filePath) {
|
|
113
129
|
const parent = path.dirname(filePath);
|
|
114
130
|
fs.mkdirSync(parent, { recursive: true });
|
|
@@ -1241,6 +1257,7 @@ async function resolveAccessTokenForCommand(baseURL, timeoutSeconds) {
|
|
|
1241
1257
|
async function runCtxpackPull(flags) {
|
|
1242
1258
|
const workspaceMeta = loadWorkspaceMeta(process.cwd());
|
|
1243
1259
|
const projectID = String(flags["project-id"] || workspaceMeta.project_id || "").trim();
|
|
1260
|
+
const workspaceDir = resolveWorkspaceDir(flags["workspace-dir"] || process.cwd());
|
|
1244
1261
|
if (!projectID) {
|
|
1245
1262
|
process.stderr.write("Missing project_id. Use --project-id <uuid> or run inside a synced ctxpack workspace.\n");
|
|
1246
1263
|
process.exitCode = 1;
|
|
@@ -1271,6 +1288,7 @@ async function runCtxpackPull(flags) {
|
|
|
1271
1288
|
timeoutSeconds,
|
|
1272
1289
|
includeCtxpack: true,
|
|
1273
1290
|
syncCtxpackLocal: true,
|
|
1291
|
+
workspaceDir,
|
|
1274
1292
|
});
|
|
1275
1293
|
process.stdout.write(`${buildProjectSummaryText(summary)}\n`);
|
|
1276
1294
|
|
|
@@ -1457,6 +1475,7 @@ async function runDoctor(flags) {
|
|
|
1457
1475
|
timeoutSeconds,
|
|
1458
1476
|
includeCtxpack: true,
|
|
1459
1477
|
syncCtxpackLocal: true,
|
|
1478
|
+
workspaceDir: context.workspaceDir,
|
|
1460
1479
|
});
|
|
1461
1480
|
if (String(summary.access || "") !== "granted") {
|
|
1462
1481
|
addDoctorCheck(
|
|
@@ -1832,19 +1851,22 @@ function hasAllCtxpackFiles(baseDir, files) {
|
|
|
1832
1851
|
return true;
|
|
1833
1852
|
}
|
|
1834
1853
|
|
|
1835
|
-
function syncCtxpackToLocalCache({ siteBaseURL, projectID, ctxpack }) {
|
|
1854
|
+
function syncCtxpackToLocalCache({ siteBaseURL, projectID, ctxpack, workspaceDir }) {
|
|
1836
1855
|
const ctxpackID = String(ctxpack?.ctxpack_id || "").trim();
|
|
1837
1856
|
const version = String(ctxpack?.version || "").trim();
|
|
1838
1857
|
const status = String(ctxpack?.status || "").trim() || "draft";
|
|
1839
1858
|
const files = normalizeCtxpackFiles(ctxpack?.files);
|
|
1840
|
-
const
|
|
1859
|
+
const resolvedWorkspaceDir = resolveWorkspaceDir(workspaceDir);
|
|
1860
|
+
const cacheDir = path.join(ctxpackCacheRootDir(resolvedWorkspaceDir), projectID);
|
|
1841
1861
|
const metaPath = path.join(cacheDir, CTXPACK_META_FILENAME);
|
|
1862
|
+
const workspaceMetaPath = path.join(resolvedWorkspaceDir, CTXPACK_META_FILENAME);
|
|
1842
1863
|
|
|
1843
1864
|
if (!ctxpackID || !version || files.length === 0) {
|
|
1844
1865
|
return {
|
|
1845
1866
|
sync_status: "not_available",
|
|
1846
1867
|
sync_message: "Ctxpack is missing or has no files on server.",
|
|
1847
1868
|
local_path: cacheDir,
|
|
1869
|
+
workspace_path: resolvedWorkspaceDir,
|
|
1848
1870
|
local_file_count: 0,
|
|
1849
1871
|
};
|
|
1850
1872
|
}
|
|
@@ -1860,6 +1882,7 @@ function syncCtxpackToLocalCache({ siteBaseURL, projectID, ctxpack }) {
|
|
|
1860
1882
|
sync_status: "current",
|
|
1861
1883
|
sync_message: "Local ctxpack cache is up to date.",
|
|
1862
1884
|
local_path: cacheDir,
|
|
1885
|
+
workspace_path: resolvedWorkspaceDir,
|
|
1863
1886
|
local_file_count: files.length,
|
|
1864
1887
|
};
|
|
1865
1888
|
}
|
|
@@ -1885,6 +1908,7 @@ function syncCtxpackToLocalCache({ siteBaseURL, projectID, ctxpack }) {
|
|
|
1885
1908
|
synced_at: new Date().toISOString(),
|
|
1886
1909
|
};
|
|
1887
1910
|
fs.writeFileSync(metaPath, `${JSON.stringify(payload, null, 2)}\n`, "utf8");
|
|
1911
|
+
fs.writeFileSync(workspaceMetaPath, `${JSON.stringify(payload, null, 2)}\n`, "utf8");
|
|
1888
1912
|
|
|
1889
1913
|
return {
|
|
1890
1914
|
sync_status: previousMeta ? "updated" : "downloaded",
|
|
@@ -1892,6 +1916,7 @@ function syncCtxpackToLocalCache({ siteBaseURL, projectID, ctxpack }) {
|
|
|
1892
1916
|
? "Ctxpack updated to latest server version."
|
|
1893
1917
|
: "Ctxpack downloaded to local cache.",
|
|
1894
1918
|
local_path: cacheDir,
|
|
1919
|
+
workspace_path: resolvedWorkspaceDir,
|
|
1895
1920
|
local_file_count: files.length,
|
|
1896
1921
|
};
|
|
1897
1922
|
} catch (err) {
|
|
@@ -1899,6 +1924,7 @@ function syncCtxpackToLocalCache({ siteBaseURL, projectID, ctxpack }) {
|
|
|
1899
1924
|
sync_status: "error",
|
|
1900
1925
|
sync_message: String(err?.message || err),
|
|
1901
1926
|
local_path: cacheDir,
|
|
1927
|
+
workspace_path: resolvedWorkspaceDir,
|
|
1902
1928
|
local_file_count: 0,
|
|
1903
1929
|
};
|
|
1904
1930
|
}
|
|
@@ -1911,6 +1937,7 @@ async function loadProjectSummaryForTool({
|
|
|
1911
1937
|
timeoutSeconds,
|
|
1912
1938
|
includeCtxpack,
|
|
1913
1939
|
syncCtxpackLocal,
|
|
1940
|
+
workspaceDir,
|
|
1914
1941
|
}) {
|
|
1915
1942
|
const encodedProjectID = encodeURIComponent(projectID);
|
|
1916
1943
|
let projectRaw = null;
|
|
@@ -1968,6 +1995,7 @@ async function loadProjectSummaryForTool({
|
|
|
1968
1995
|
siteBaseURL,
|
|
1969
1996
|
projectID,
|
|
1970
1997
|
ctxpack,
|
|
1998
|
+
workspaceDir,
|
|
1971
1999
|
});
|
|
1972
2000
|
ctxpackSyncStatus = syncResult.sync_status || "error";
|
|
1973
2001
|
ctxpackSyncMessage = String(syncResult.sync_message || "").trim();
|
|
@@ -2008,6 +2036,7 @@ async function loadProjectSummaryForTool({
|
|
|
2008
2036
|
ctxpack_sync_message: ctxpackSyncMessage,
|
|
2009
2037
|
ctxpack_local_path: ctxpackLocalPath,
|
|
2010
2038
|
ctxpack_local_file_count: ctxpackLocalFileCount,
|
|
2039
|
+
ctxpack_workspace_path: String(workspaceDir || "").trim(),
|
|
2011
2040
|
agenda,
|
|
2012
2041
|
agenda_source: agendaSource,
|
|
2013
2042
|
};
|
|
@@ -2149,6 +2178,7 @@ async function appendWorkitemListHints(responseObj, args, toolArgs, token) {
|
|
|
2149
2178
|
timeoutSeconds: args.timeoutSeconds,
|
|
2150
2179
|
includeCtxpack: isEmptyBody,
|
|
2151
2180
|
syncCtxpackLocal: isEmptyBody,
|
|
2181
|
+
workspaceDir: args.workspaceDir,
|
|
2152
2182
|
});
|
|
2153
2183
|
if (String(summary.access || "") === "granted") {
|
|
2154
2184
|
nextLines.push("Project context:");
|
|
@@ -2186,12 +2216,66 @@ async function appendWorkitemListHints(responseObj, args, toolArgs, token) {
|
|
|
2186
2216
|
return responseObj;
|
|
2187
2217
|
}
|
|
2188
2218
|
|
|
2219
|
+
function appendCtxpackEnsureSyncHints(responseObj, args, toolArgs) {
|
|
2220
|
+
const result = safeObject(responseObj.result);
|
|
2221
|
+
const content = ensureArray(result.content);
|
|
2222
|
+
if (!content.length) return responseObj;
|
|
2223
|
+
|
|
2224
|
+
const first = safeObject(content[0]);
|
|
2225
|
+
if (String(first.type || "") !== "text") return responseObj;
|
|
2226
|
+
const text = String(first.text || "").trim();
|
|
2227
|
+
if (!text) return responseObj;
|
|
2228
|
+
|
|
2229
|
+
const envelope = parseToolEnvelopeFromRPCResult(result);
|
|
2230
|
+
if (!envelope) return responseObj;
|
|
2231
|
+
if (String(envelope.tool || "").trim() !== "ctxpack.ensure") return responseObj;
|
|
2232
|
+
if (!Boolean(envelope.ok) || Number(envelope.status || 0) !== 200) return responseObj;
|
|
2233
|
+
|
|
2234
|
+
const body = safeObject(envelope.body);
|
|
2235
|
+
if (!Object.keys(body).length) return responseObj;
|
|
2236
|
+
|
|
2237
|
+
const projectID = String(
|
|
2238
|
+
toolArgs?.project_id || toolArgs?.projectID || envelope.project_id || args.projectID || "",
|
|
2239
|
+
).trim();
|
|
2240
|
+
if (!projectID || !isUUID(projectID)) return responseObj;
|
|
2241
|
+
|
|
2242
|
+
const syncResult = syncCtxpackToLocalCache({
|
|
2243
|
+
siteBaseURL: normalizeSiteBaseURL(args.baseURL),
|
|
2244
|
+
projectID,
|
|
2245
|
+
ctxpack: body,
|
|
2246
|
+
workspaceDir: args.workspaceDir,
|
|
2247
|
+
});
|
|
2248
|
+
|
|
2249
|
+
const lines = [
|
|
2250
|
+
"",
|
|
2251
|
+
`ctxpack_local_sync_status: ${String(syncResult.sync_status || "error")}`,
|
|
2252
|
+
];
|
|
2253
|
+
if (syncResult.sync_message) {
|
|
2254
|
+
lines.push(`ctxpack_local_sync_message: ${String(syncResult.sync_message)}`);
|
|
2255
|
+
}
|
|
2256
|
+
if (syncResult.local_path) {
|
|
2257
|
+
lines.push(`ctxpack_local_path: ${String(syncResult.local_path)}`);
|
|
2258
|
+
}
|
|
2259
|
+
if (syncResult.workspace_path) {
|
|
2260
|
+
lines.push(`ctxpack_workspace_path: ${String(syncResult.workspace_path)}`);
|
|
2261
|
+
}
|
|
2262
|
+
|
|
2263
|
+
content[0] = {
|
|
2264
|
+
...first,
|
|
2265
|
+
text: `${text}\n${lines.join("\n")}`,
|
|
2266
|
+
};
|
|
2267
|
+
result.content = content;
|
|
2268
|
+
responseObj.result = result;
|
|
2269
|
+
return responseObj;
|
|
2270
|
+
}
|
|
2271
|
+
|
|
2189
2272
|
async function runProxy(flags) {
|
|
2190
2273
|
const workspaceMeta = loadWorkspaceMeta(process.cwd());
|
|
2191
2274
|
const args = {
|
|
2192
2275
|
baseURL: flags["base-url"] || DEFAULT_BASE_URL,
|
|
2193
2276
|
projectID: String(flags["project-id"] || workspaceMeta.project_id || "").trim(),
|
|
2194
2277
|
ctxpackKey: String(flags["ctxpack-key"] || buildCtxpackKeyFromMeta(workspaceMeta) || "").trim(),
|
|
2278
|
+
workspaceDir: resolveWorkspaceDir(flags["workspace-dir"] || process.cwd()),
|
|
2195
2279
|
includeDrafts: boolFromRaw(flags["include-drafts"], true),
|
|
2196
2280
|
timeoutSeconds: intFromRaw(flags["timeout-seconds"], 30),
|
|
2197
2281
|
};
|
|
@@ -2289,6 +2373,7 @@ async function runProxy(flags) {
|
|
|
2289
2373
|
timeoutSeconds: args.timeoutSeconds,
|
|
2290
2374
|
includeCtxpack,
|
|
2291
2375
|
syncCtxpackLocal,
|
|
2376
|
+
workspaceDir: args.workspaceDir,
|
|
2292
2377
|
});
|
|
2293
2378
|
const text = buildProjectSummaryText(summary);
|
|
2294
2379
|
process.stdout.write(
|
|
@@ -2324,6 +2409,8 @@ async function runProxy(flags) {
|
|
|
2324
2409
|
patched = appendProjectHintToInitialize(patched, args);
|
|
2325
2410
|
} else if (isJsonRpcMethod(requestObj, "tools/call") && toolName === "workitem.list") {
|
|
2326
2411
|
patched = await appendWorkitemListHints(patched, args, toolArgs, token);
|
|
2412
|
+
} else if (isJsonRpcMethod(requestObj, "tools/call") && toolName === "ctxpack.ensure") {
|
|
2413
|
+
patched = appendCtxpackEnsureSyncHints(patched, args, toolArgs);
|
|
2327
2414
|
}
|
|
2328
2415
|
process.stdout.write(`${JSON.stringify(patched)}\n`);
|
|
2329
2416
|
return;
|
|
@@ -2408,11 +2495,13 @@ function resolveSetupContext(flags) {
|
|
|
2408
2495
|
const projectID = String(flags["project-id"] || workspaceMeta.project_id || "").trim();
|
|
2409
2496
|
const ctxpackKey = String(flags["ctxpack-key"] || buildCtxpackKeyFromMeta(workspaceMeta) || "").trim();
|
|
2410
2497
|
const baseURL = String(flags["base-url"] || DEFAULT_SITE_URL).trim().replace(/\/+$/, "");
|
|
2498
|
+
const workspaceDir = resolveWorkspaceDir(flags["workspace-dir"] || process.cwd());
|
|
2411
2499
|
const serverName = String(flags.name || DEFAULT_SERVER_NAME).trim() || DEFAULT_SERVER_NAME;
|
|
2412
2500
|
const proxyArgs = ["--base-url", `${baseURL}/governance/mcp`];
|
|
2501
|
+
proxyArgs.push("--workspace-dir", workspaceDir);
|
|
2413
2502
|
if (projectID) proxyArgs.push("--project-id", projectID);
|
|
2414
2503
|
if (ctxpackKey) proxyArgs.push("--ctxpack-key", ctxpackKey);
|
|
2415
|
-
return { projectID, ctxpackKey, baseURL, serverName, proxyArgs };
|
|
2504
|
+
return { projectID, ctxpackKey, baseURL, workspaceDir, serverName, proxyArgs };
|
|
2416
2505
|
}
|
|
2417
2506
|
|
|
2418
2507
|
function runSetupInternal(flags, options = {}) {
|
|
@@ -2424,20 +2513,24 @@ function runSetupInternal(flags, options = {}) {
|
|
|
2424
2513
|
for (const cliBin of clients) {
|
|
2425
2514
|
if (!commandExists(cliBin)) continue;
|
|
2426
2515
|
const alreadyRegistered = isRegistered(cliBin, context.serverName);
|
|
2427
|
-
if (ensureOnly
|
|
2428
|
-
results.push({ cliBin, ok: true, action: "already-registered" });
|
|
2429
|
-
continue;
|
|
2430
|
-
}
|
|
2431
|
-
if (!ensureOnly) {
|
|
2516
|
+
if (!ensureOnly || alreadyRegistered) {
|
|
2432
2517
|
runRemove(cliBin, context.serverName);
|
|
2433
2518
|
}
|
|
2434
2519
|
const ok = tryRegister(cliBin, context.serverName, context.proxyArgs);
|
|
2435
|
-
|
|
2520
|
+
const action = ensureOnly
|
|
2521
|
+
? alreadyRegistered
|
|
2522
|
+
? "updated"
|
|
2523
|
+
: "registered"
|
|
2524
|
+
: alreadyRegistered
|
|
2525
|
+
? "re-registered"
|
|
2526
|
+
: "registered";
|
|
2527
|
+
results.push({ cliBin, ok, action });
|
|
2436
2528
|
}
|
|
2437
2529
|
|
|
2438
2530
|
process.stdout.write(ensureOnly ? "\nEnsure complete.\n" : "\nInstall complete.\n");
|
|
2439
2531
|
process.stdout.write(`Server: ${context.serverName}\n`);
|
|
2440
2532
|
process.stdout.write(`Gateway: ${context.baseURL}/governance/mcp\n`);
|
|
2533
|
+
process.stdout.write(`Workspace: ${context.workspaceDir}\n`);
|
|
2441
2534
|
process.stdout.write(`Project: ${context.projectID || "auto-detect from .metheus_ctxpack_sync.json"}\n`);
|
|
2442
2535
|
if (context.ctxpackKey) {
|
|
2443
2536
|
process.stdout.write(`Ctxpack: ${context.ctxpackKey}\n`);
|