metheus-governance-mcp-cli 0.2.33 → 0.2.35
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 +91 -0
- package/package.json +1 -1
package/cli.mjs
CHANGED
|
@@ -4031,11 +4031,71 @@ async function runProxy(flags) {
|
|
|
4031
4031
|
timeoutSeconds: intFromRaw(flags["timeout-seconds"], 30),
|
|
4032
4032
|
};
|
|
4033
4033
|
const gatewayURL = buildGatewayURL(args);
|
|
4034
|
+
// Diagnostic: log workspace resolution context to file for debugging.
|
|
4035
|
+
try {
|
|
4036
|
+
const _diagDir = path.join(String(process.env.USERPROFILE || process.env.HOME || "."), ".metheus");
|
|
4037
|
+
fs.mkdirSync(_diagDir, { recursive: true });
|
|
4038
|
+
fs.appendFileSync(
|
|
4039
|
+
path.join(_diagDir, "mcp-proxy-diag.log"),
|
|
4040
|
+
`[${new Date().toISOString()}] boot: cwd=${process.cwd()} workspace-dir=${workspaceDirRaw} autoMode=${workspaceAutoMode} pinned=${pinnedWorkspaceDir || "(none)"} METHEUS_WORKSPACE_DIR=${process.env.METHEUS_WORKSPACE_DIR || "(unset)"}\n`,
|
|
4041
|
+
);
|
|
4042
|
+
} catch { /* best-effort */ }
|
|
4034
4043
|
let lastRefreshAttemptAtMs = 0;
|
|
4035
4044
|
let lastRefreshError = "";
|
|
4036
4045
|
let sessionWorkspaceDir = "";
|
|
4037
4046
|
let sessionWorkspaceTrusted = false;
|
|
4038
4047
|
|
|
4048
|
+
// Proxy-initiated requests (e.g., roots/list) pending client responses.
|
|
4049
|
+
const pendingProxyRequests = new Map(); // id → callback(responseObj)
|
|
4050
|
+
let proxyRequestIdCounter = 0;
|
|
4051
|
+
|
|
4052
|
+
/**
|
|
4053
|
+
* Probe the MCP client for workspace roots via the standard roots/list request.
|
|
4054
|
+
* This allows the proxy to discover the actual workspace folder even when the client
|
|
4055
|
+
* (e.g., Antigravity) sets process.cwd() to its install directory instead of the workspace.
|
|
4056
|
+
*/
|
|
4057
|
+
function sendRootsListProbe() {
|
|
4058
|
+
const id = `_proxy_roots_${++proxyRequestIdCounter}`;
|
|
4059
|
+
pendingProxyRequests.set(id, (response) => {
|
|
4060
|
+
const roots = Array.isArray(response?.result?.roots) ? response.result.roots : [];
|
|
4061
|
+
let foundWorkspace = "";
|
|
4062
|
+
for (const root of roots) {
|
|
4063
|
+
const candidate = sanitizeWorkspaceCandidate(String(root?.uri || root?.path || root?.name || ""));
|
|
4064
|
+
if (candidate) {
|
|
4065
|
+
foundWorkspace = candidate;
|
|
4066
|
+
break;
|
|
4067
|
+
}
|
|
4068
|
+
}
|
|
4069
|
+
try {
|
|
4070
|
+
const _diagDir = path.join(String(process.env.USERPROFILE || process.env.HOME || "."), ".metheus");
|
|
4071
|
+
fs.appendFileSync(
|
|
4072
|
+
path.join(_diagDir, "mcp-proxy-diag.log"),
|
|
4073
|
+
foundWorkspace
|
|
4074
|
+
? `[${new Date().toISOString()}] roots/list: workspace=${foundWorkspace} from ${roots.length} root(s)\n`
|
|
4075
|
+
: `[${new Date().toISOString()}] roots/list: no valid workspace from ${roots.length} root(s), raw=${JSON.stringify(roots).substring(0, 300)}\n`,
|
|
4076
|
+
);
|
|
4077
|
+
} catch { /* best-effort */ }
|
|
4078
|
+
if (foundWorkspace) {
|
|
4079
|
+
sessionWorkspaceDir = foundWorkspace;
|
|
4080
|
+
sessionWorkspaceTrusted = true;
|
|
4081
|
+
}
|
|
4082
|
+
});
|
|
4083
|
+
// Timeout: if client does not respond within 10 seconds, discard the pending callback.
|
|
4084
|
+
setTimeout(() => {
|
|
4085
|
+
if (pendingProxyRequests.has(id)) {
|
|
4086
|
+
pendingProxyRequests.delete(id);
|
|
4087
|
+
try {
|
|
4088
|
+
const _diagDir = path.join(String(process.env.USERPROFILE || process.env.HOME || "."), ".metheus");
|
|
4089
|
+
fs.appendFileSync(
|
|
4090
|
+
path.join(_diagDir, "mcp-proxy-diag.log"),
|
|
4091
|
+
`[${new Date().toISOString()}] roots/list: timeout (client did not respond)\n`,
|
|
4092
|
+
);
|
|
4093
|
+
} catch { /* best-effort */ }
|
|
4094
|
+
}
|
|
4095
|
+
}, 10000);
|
|
4096
|
+
process.stdout.write(`${JSON.stringify({ jsonrpc: "2.0", id, method: "roots/list" })}\n`);
|
|
4097
|
+
}
|
|
4098
|
+
|
|
4039
4099
|
const handleIncomingMessage = async (lineRaw) => {
|
|
4040
4100
|
const line = String(lineRaw || "").trim();
|
|
4041
4101
|
if (!line) return;
|
|
@@ -4046,6 +4106,15 @@ async function runProxy(flags) {
|
|
|
4046
4106
|
return;
|
|
4047
4107
|
}
|
|
4048
4108
|
|
|
4109
|
+
// Intercept responses to proxy-initiated requests (e.g., roots/list).
|
|
4110
|
+
// These have an id but no method field — they are client responses, not new requests.
|
|
4111
|
+
if (!requestObj.method && requestObj.id != null && pendingProxyRequests.has(String(requestObj.id))) {
|
|
4112
|
+
const callback = pendingProxyRequests.get(String(requestObj.id));
|
|
4113
|
+
pendingProxyRequests.delete(String(requestObj.id));
|
|
4114
|
+
try { callback(requestObj); } catch { /* best-effort */ }
|
|
4115
|
+
return;
|
|
4116
|
+
}
|
|
4117
|
+
|
|
4049
4118
|
let resolved = resolveCurrentAccessToken();
|
|
4050
4119
|
if (!resolved.token) {
|
|
4051
4120
|
const nowMs = Date.now();
|
|
@@ -4131,6 +4200,15 @@ async function runProxy(flags) {
|
|
|
4131
4200
|
requestObj,
|
|
4132
4201
|
toolArgs,
|
|
4133
4202
|
);
|
|
4203
|
+
if (isJsonRpcMethod(requestObj, "tools/call")) {
|
|
4204
|
+
try {
|
|
4205
|
+
const _diagDir = path.join(String(process.env.USERPROFILE || process.env.HOME || "."), ".metheus");
|
|
4206
|
+
fs.appendFileSync(
|
|
4207
|
+
path.join(_diagDir, "mcp-proxy-diag.log"),
|
|
4208
|
+
`[${new Date().toISOString()}] tools/call: tool=${toolName} sessionWS=${sessionWorkspaceDir} resolvedWS=${requestWorkspaceDir} trusted=${workspaceSignalTrusted} strongReq=${strongRequestWorkspaceCandidate || "(none)"} strongEnv=${strongEnvWorkspaceCandidate || "(none)"}\n`,
|
|
4209
|
+
);
|
|
4210
|
+
} catch { /* best-effort */ }
|
|
4211
|
+
}
|
|
4134
4212
|
let autoSyncSummary = null;
|
|
4135
4213
|
if (isJsonRpcMethod(requestObj, "tools/call")) {
|
|
4136
4214
|
autoSyncSummary = await maybeAutoSyncCtxpackForCall({
|
|
@@ -4358,6 +4436,19 @@ async function runProxy(flags) {
|
|
|
4358
4436
|
patched = appendLocalToolToToolsList(patched);
|
|
4359
4437
|
} else if (isJsonRpcMethod(requestObj, "initialize")) {
|
|
4360
4438
|
patched = appendProjectHintToInitialize(patched, args);
|
|
4439
|
+
// Log initialize params for workspace debugging.
|
|
4440
|
+
try {
|
|
4441
|
+
const _diagDir = path.join(String(process.env.USERPROFILE || process.env.HOME || "."), ".metheus");
|
|
4442
|
+
const initParams = safeObject(requestObj?.params);
|
|
4443
|
+
fs.appendFileSync(
|
|
4444
|
+
path.join(_diagDir, "mcp-proxy-diag.log"),
|
|
4445
|
+
`[${new Date().toISOString()}] initialize: clientInfo=${JSON.stringify(initParams.clientInfo || {})} capabilities=${JSON.stringify(initParams.capabilities || {})} rootUri=${initParams.rootUri || "(none)"} workspaceFolders=${JSON.stringify(initParams.workspaceFolders || []).substring(0, 300)}\n`,
|
|
4446
|
+
);
|
|
4447
|
+
} catch { /* best-effort */ }
|
|
4448
|
+
// Probe client workspace roots via MCP roots/list protocol.
|
|
4449
|
+
// Use setImmediate so the initialize response is flushed to stdout first;
|
|
4450
|
+
// the client must see the response before it can handle server-initiated requests.
|
|
4451
|
+
setImmediate(() => sendRootsListProbe());
|
|
4361
4452
|
} else if (isJsonRpcMethod(requestObj, "tools/call") && toolName === "workitem.list") {
|
|
4362
4453
|
patched = await appendWorkitemListHints(patched, args, toolArgs, token, workspaceSignalTrusted);
|
|
4363
4454
|
} else if (isJsonRpcMethod(requestObj, "tools/call") && toolName === "ctxpack.ensure") {
|