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.
Files changed (2) hide show
  1. package/cli.mjs +91 -0
  2. 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") {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "metheus-governance-mcp-cli",
3
- "version": "0.2.33",
3
+ "version": "0.2.35",
4
4
  "description": "Metheus Governance MCP CLI (setup + stdio proxy)",
5
5
  "type": "module",
6
6
  "files": [