metheus-governance-mcp-cli 0.2.34 → 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 +73 -0
- package/package.json +1 -1
package/cli.mjs
CHANGED
|
@@ -4045,6 +4045,57 @@ async function runProxy(flags) {
|
|
|
4045
4045
|
let sessionWorkspaceDir = "";
|
|
4046
4046
|
let sessionWorkspaceTrusted = false;
|
|
4047
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
|
+
|
|
4048
4099
|
const handleIncomingMessage = async (lineRaw) => {
|
|
4049
4100
|
const line = String(lineRaw || "").trim();
|
|
4050
4101
|
if (!line) return;
|
|
@@ -4055,6 +4106,15 @@ async function runProxy(flags) {
|
|
|
4055
4106
|
return;
|
|
4056
4107
|
}
|
|
4057
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
|
+
|
|
4058
4118
|
let resolved = resolveCurrentAccessToken();
|
|
4059
4119
|
if (!resolved.token) {
|
|
4060
4120
|
const nowMs = Date.now();
|
|
@@ -4376,6 +4436,19 @@ async function runProxy(flags) {
|
|
|
4376
4436
|
patched = appendLocalToolToToolsList(patched);
|
|
4377
4437
|
} else if (isJsonRpcMethod(requestObj, "initialize")) {
|
|
4378
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());
|
|
4379
4452
|
} else if (isJsonRpcMethod(requestObj, "tools/call") && toolName === "workitem.list") {
|
|
4380
4453
|
patched = await appendWorkitemListHints(patched, args, toolArgs, token, workspaceSignalTrusted);
|
|
4381
4454
|
} else if (isJsonRpcMethod(requestObj, "tools/call") && toolName === "ctxpack.ensure") {
|