metheus-governance-mcp-cli 0.2.15 → 0.2.16
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/README.md +23 -1
- package/cli.mjs +72 -22
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -38,6 +38,25 @@ metheus-governance-mcp-cli setup --project-id <project_uuid> --ctxpack-key "<ctx
|
|
|
38
38
|
|
|
39
39
|
`project-id` can be omitted if your current folder (or parent) has `.metheus_ctxpack_sync.json`.
|
|
40
40
|
|
|
41
|
+
Recommended for Codex/Claude multi-workspace sessions:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
metheus-governance-mcp-cli setup --project-id <project_uuid> --ctxpack-key "<ctxpack_key>" --base-url https://metheus.gesiaplatform.com --workspace-dir auto
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Guardrail note:
|
|
48
|
+
- By default, CLI blocks reading/writing ctxpack sync metadata when workspace root resolves to the home directory.
|
|
49
|
+
- Override only when intentional: `METHEUS_ALLOW_HOME_WORKSPACE=1`.
|
|
50
|
+
|
|
51
|
+
## Project ID behavior (verified)
|
|
52
|
+
|
|
53
|
+
- `setup --project-id <A>` sets default proxy scope to project `A`.
|
|
54
|
+
- Proxy forwards `project_id` to gateway query, and gateway sets `MCP_PROJECT_ID` for MCP runtime.
|
|
55
|
+
- If a tool call includes `project_id` in its arguments, that value overrides setup-time default scope.
|
|
56
|
+
- If tool arguments omit `project_id`, server falls back to `MCP_PROJECT_ID`.
|
|
57
|
+
- If both are missing, the call fails with `project_id is required`.
|
|
58
|
+
- For single-project sessions, keep one pinned `--project-id` and avoid sending a different `project_id` unless intentional.
|
|
59
|
+
|
|
41
60
|
## Doctor
|
|
42
61
|
|
|
43
62
|
```bash
|
|
@@ -74,7 +93,8 @@ These tools accept `project_id` and return:
|
|
|
74
93
|
- missing local cache -> download
|
|
75
94
|
- same version -> keep current
|
|
76
95
|
- newer server version -> update local cache
|
|
77
|
-
- workspace path ->
|
|
96
|
+
- workspace path -> pinned to setup/proxy working directory by default
|
|
97
|
+
- use `--workspace-dir auto` to follow active client workspace/root dynamically
|
|
78
98
|
|
|
79
99
|
Ctxpack merge safety flow:
|
|
80
100
|
- call `ctxpack.merge.brief` first
|
|
@@ -200,6 +220,8 @@ Actual publish:
|
|
|
200
220
|
npm run publish:public
|
|
201
221
|
```
|
|
202
222
|
|
|
223
|
+
If npm returns `You cannot publish over the previously published versions`, bump `package.json` version and retry.
|
|
224
|
+
|
|
203
225
|
If npm account uses 2FA, pass OTP:
|
|
204
226
|
|
|
205
227
|
```bash
|
package/cli.mjs
CHANGED
|
@@ -25,6 +25,7 @@ const SELF_UPDATE_CHECK_INTERVAL_MS = 6 * 60 * 60 * 1000;
|
|
|
25
25
|
const SELF_UPDATE_TIMEOUT_SECONDS = 6;
|
|
26
26
|
const SELF_UPDATE_ENV_KEY = "METHEUS_CLI_AUTO_UPDATE";
|
|
27
27
|
const SELF_UPDATE_GUARD_ENV_KEY = "METHEUS_CLI_SELF_UPDATE_ATTEMPTED";
|
|
28
|
+
const ALLOW_HOME_WORKSPACE_ENV_KEY = "METHEUS_ALLOW_HOME_WORKSPACE";
|
|
28
29
|
const AUTO_CTXPACK_SYNC_INTERVAL_MS = 60 * 1000;
|
|
29
30
|
const autoCtxpackSyncTracker = new Map();
|
|
30
31
|
|
|
@@ -50,6 +51,7 @@ function printUsage() {
|
|
|
50
51
|
"Environment:",
|
|
51
52
|
" METHEUS_TOKEN or MCP_AUTH_TOKEN is used first for proxy requests.",
|
|
52
53
|
` ${SELF_UPDATE_ENV_KEY}=0 to disable startup auto-update check.`,
|
|
54
|
+
` ${ALLOW_HOME_WORKSPACE_ENV_KEY}=1 to allow using home directory as workspace root (disabled by default).`,
|
|
53
55
|
" If env is missing, stored token file is used:",
|
|
54
56
|
` ${AUTH_STORE_RELATIVE_PATH}`,
|
|
55
57
|
"",
|
|
@@ -423,6 +425,29 @@ function resolveWorkspaceDirForRequest(defaultWorkspaceDir, requestObj, toolArgs
|
|
|
423
425
|
return resolveWorkspaceDir(process.cwd());
|
|
424
426
|
}
|
|
425
427
|
|
|
428
|
+
function normalizedPathForCompare(rawPath) {
|
|
429
|
+
const candidate = sanitizeWorkspaceCandidate(rawPath);
|
|
430
|
+
if (!candidate) return "";
|
|
431
|
+
const normalized = path.normalize(candidate);
|
|
432
|
+
return process.platform === "win32" ? normalized.toLowerCase() : normalized;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
function homeWorkspaceRoot() {
|
|
436
|
+
return sanitizeWorkspaceCandidate(firstNonEmptyString([process.env.USERPROFILE, process.env.HOME]));
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
function isHomeWorkspaceRoot(workspaceDir) {
|
|
440
|
+
const home = normalizedPathForCompare(homeWorkspaceRoot());
|
|
441
|
+
if (!home) return false;
|
|
442
|
+
const current = normalizedPathForCompare(workspaceDir);
|
|
443
|
+
if (!current) return false;
|
|
444
|
+
return current === home;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
function allowHomeWorkspaceRoot() {
|
|
448
|
+
return boolFromRaw(process.env[ALLOW_HOME_WORKSPACE_ENV_KEY], false);
|
|
449
|
+
}
|
|
450
|
+
|
|
426
451
|
function resolveProjectIDForRequest({
|
|
427
452
|
toolArgs,
|
|
428
453
|
args,
|
|
@@ -437,8 +462,8 @@ function resolveProjectIDForRequest({
|
|
|
437
462
|
toolArgs?.projectID,
|
|
438
463
|
responseProjectID,
|
|
439
464
|
envelopeProjectID,
|
|
440
|
-
workspaceMeta.project_id,
|
|
441
465
|
args?.projectID,
|
|
466
|
+
workspaceMeta.project_id,
|
|
442
467
|
]);
|
|
443
468
|
}
|
|
444
469
|
|
|
@@ -1172,7 +1197,11 @@ function findSyncMetaFile(startDir) {
|
|
|
1172
1197
|
}
|
|
1173
1198
|
|
|
1174
1199
|
function loadWorkspaceMeta(startDir) {
|
|
1175
|
-
const
|
|
1200
|
+
const resolvedStart = resolveWorkspaceDir(startDir || process.cwd());
|
|
1201
|
+
if (isHomeWorkspaceRoot(resolvedStart) && !allowHomeWorkspaceRoot()) {
|
|
1202
|
+
return {};
|
|
1203
|
+
}
|
|
1204
|
+
const metaFile = findSyncMetaFile(resolvedStart);
|
|
1176
1205
|
if (!metaFile) return {};
|
|
1177
1206
|
try {
|
|
1178
1207
|
const raw = fs.readFileSync(metaFile, "utf8");
|
|
@@ -2418,6 +2447,17 @@ function syncCtxpackToLocalCache({ siteBaseURL, projectID, ctxpack, workspaceDir
|
|
|
2418
2447
|
const metaPath = path.join(cacheDir, CTXPACK_META_FILENAME);
|
|
2419
2448
|
const workspaceMetaPath = path.join(resolvedWorkspaceDir, CTXPACK_META_FILENAME);
|
|
2420
2449
|
|
|
2450
|
+
if (isHomeWorkspaceRoot(resolvedWorkspaceDir) && !allowHomeWorkspaceRoot()) {
|
|
2451
|
+
return {
|
|
2452
|
+
sync_status: "guarded",
|
|
2453
|
+
sync_message:
|
|
2454
|
+
"Workspace root resolved to home directory. Guardrail blocked ctxpack local write. Use --workspace-dir <project_path> or --workspace-dir auto.",
|
|
2455
|
+
local_path: cacheDir,
|
|
2456
|
+
workspace_path: resolvedWorkspaceDir,
|
|
2457
|
+
local_file_count: 0,
|
|
2458
|
+
};
|
|
2459
|
+
}
|
|
2460
|
+
|
|
2421
2461
|
if (!ctxpackID || !version || files.length === 0) {
|
|
2422
2462
|
return {
|
|
2423
2463
|
sync_status: "not_available",
|
|
@@ -3622,13 +3662,20 @@ async function appendCtxpackConflictHintToErrorResponse(responseObj, args, toolN
|
|
|
3622
3662
|
}
|
|
3623
3663
|
|
|
3624
3664
|
async function runProxy(flags) {
|
|
3625
|
-
const
|
|
3626
|
-
const
|
|
3665
|
+
const workspaceDirRaw = String(flags["workspace-dir"] || "").trim();
|
|
3666
|
+
const hasWorkspaceDirFlag = Object.prototype.hasOwnProperty.call(flags, "workspace-dir");
|
|
3667
|
+
const workspaceAutoMode = hasWorkspaceDirFlag && workspaceDirRaw.length > 0 && isAutoWorkspaceMode(workspaceDirRaw);
|
|
3668
|
+
const explicitPinnedWorkspace = hasWorkspaceDirFlag && !workspaceAutoMode;
|
|
3669
|
+
// Keep pinned workspace only when user explicitly set --workspace-dir.
|
|
3670
|
+
// If not explicitly set, resolve per-request from client metadata/env first.
|
|
3671
|
+
const pinnedWorkspaceDir = explicitPinnedWorkspace ? resolveWorkspaceDir(workspaceDirRaw || process.cwd()) : "";
|
|
3627
3672
|
const args = {
|
|
3628
3673
|
baseURL: flags["base-url"] || DEFAULT_BASE_URL,
|
|
3629
3674
|
projectID: String(flags["project-id"] || "").trim(),
|
|
3630
3675
|
ctxpackKey: String(flags["ctxpack-key"] || "").trim(),
|
|
3631
|
-
workspaceDir:
|
|
3676
|
+
workspaceDir: pinnedWorkspaceDir,
|
|
3677
|
+
workspaceAutoMode,
|
|
3678
|
+
explicitPinnedWorkspace,
|
|
3632
3679
|
includeDrafts: boolFromRaw(flags["include-drafts"], true),
|
|
3633
3680
|
autoPullOnConflict: boolFromRaw(flags["auto-pull-on-conflict"], true),
|
|
3634
3681
|
timeoutSeconds: intFromRaw(flags["timeout-seconds"], 30),
|
|
@@ -3694,18 +3741,22 @@ async function runProxy(flags) {
|
|
|
3694
3741
|
}
|
|
3695
3742
|
|
|
3696
3743
|
const { name: toolName, args: toolArgs } = extractToolCall(requestObj);
|
|
3697
|
-
|
|
3698
|
-
|
|
3699
|
-
|
|
3700
|
-
|
|
3701
|
-
|
|
3702
|
-
|
|
3744
|
+
if (!args.explicitPinnedWorkspace) {
|
|
3745
|
+
const requestWorkspaceCandidate = extractWorkspaceCandidateFromRequest(requestObj, toolArgs);
|
|
3746
|
+
const envWorkspaceCandidate = extractWorkspaceCandidateFromEnv();
|
|
3747
|
+
if (requestWorkspaceCandidate) {
|
|
3748
|
+
sessionWorkspaceDir = requestWorkspaceCandidate;
|
|
3749
|
+
} else if (envWorkspaceCandidate) {
|
|
3750
|
+
sessionWorkspaceDir = envWorkspaceCandidate;
|
|
3751
|
+
}
|
|
3703
3752
|
}
|
|
3704
|
-
const requestWorkspaceDir =
|
|
3705
|
-
|
|
3706
|
-
|
|
3707
|
-
|
|
3708
|
-
|
|
3753
|
+
const requestWorkspaceDir = args.explicitPinnedWorkspace
|
|
3754
|
+
? resolveWorkspaceDir(args.workspaceDir || process.cwd())
|
|
3755
|
+
: resolveWorkspaceDirForRequest(
|
|
3756
|
+
firstNonEmptyString([sessionWorkspaceDir, args.workspaceDir, process.cwd()]),
|
|
3757
|
+
requestObj,
|
|
3758
|
+
toolArgs,
|
|
3759
|
+
);
|
|
3709
3760
|
let autoSyncSummary = null;
|
|
3710
3761
|
if (isJsonRpcMethod(requestObj, "tools/call")) {
|
|
3711
3762
|
autoSyncSummary = await maybeAutoSyncCtxpackForCall({
|
|
@@ -4013,16 +4064,15 @@ function resolveSetupContext(flags) {
|
|
|
4013
4064
|
const workspaceDirRaw = String(flags["workspace-dir"] || "").trim();
|
|
4014
4065
|
const hasWorkspaceDirFlag = workspaceDirRaw.length > 0;
|
|
4015
4066
|
const workspaceAutoMode = hasWorkspaceDirFlag && isAutoWorkspaceMode(workspaceDirRaw);
|
|
4016
|
-
// Default:
|
|
4017
|
-
//
|
|
4018
|
-
const shouldPinWorkspaceDir =
|
|
4067
|
+
// Default: pin to current working directory.
|
|
4068
|
+
// Use --workspace-dir auto for dynamic runtime workspace detection.
|
|
4069
|
+
const shouldPinWorkspaceDir = !workspaceAutoMode;
|
|
4019
4070
|
const workspaceDir = resolveWorkspaceDir(
|
|
4020
|
-
shouldPinWorkspaceDir ? workspaceDirRaw : process.cwd(),
|
|
4071
|
+
shouldPinWorkspaceDir ? workspaceDirRaw || process.cwd() : process.cwd(),
|
|
4021
4072
|
);
|
|
4022
4073
|
const serverName = String(flags.name || DEFAULT_SERVER_NAME).trim() || DEFAULT_SERVER_NAME;
|
|
4023
4074
|
const proxyArgs = ["--base-url", `${baseURL}/governance/mcp`];
|
|
4024
|
-
//
|
|
4025
|
-
// Use --workspace-dir <path> when deterministic pinning is preferred.
|
|
4075
|
+
// Pin workspace by default to avoid spreading ctxpack cache across mixed client roots.
|
|
4026
4076
|
if (shouldPinWorkspaceDir) {
|
|
4027
4077
|
proxyArgs.push("--workspace-dir", workspaceDir);
|
|
4028
4078
|
}
|