metheus-governance-mcp-cli 0.2.35 → 0.2.36
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 +79 -84
- package/package.json +1 -1
package/cli.mjs
CHANGED
|
@@ -4048,6 +4048,28 @@ async function runProxy(flags) {
|
|
|
4048
4048
|
// Proxy-initiated requests (e.g., roots/list) pending client responses.
|
|
4049
4049
|
const pendingProxyRequests = new Map(); // id → callback(responseObj)
|
|
4050
4050
|
let proxyRequestIdCounter = 0;
|
|
4051
|
+
let stdoutEnvelopeMode = "line"; // "line" | "framed"
|
|
4052
|
+
|
|
4053
|
+
function writeProxyMessage(messageText) {
|
|
4054
|
+
const normalized = String(messageText || "").trim();
|
|
4055
|
+
if (!normalized) return;
|
|
4056
|
+
if (stdoutEnvelopeMode === "framed") {
|
|
4057
|
+
const contentLength = Buffer.byteLength(normalized, "utf8");
|
|
4058
|
+
process.stdout.write(`Content-Length: ${contentLength}\r\n\r\n${normalized}`);
|
|
4059
|
+
return;
|
|
4060
|
+
}
|
|
4061
|
+
process.stdout.write(`${normalized}\n`);
|
|
4062
|
+
}
|
|
4063
|
+
|
|
4064
|
+
function writeProxyJson(payload) {
|
|
4065
|
+
writeProxyMessage(JSON.stringify(payload));
|
|
4066
|
+
}
|
|
4067
|
+
|
|
4068
|
+
function clientSupportsRootsList(initParamsRaw) {
|
|
4069
|
+
const initParams = safeObject(initParamsRaw);
|
|
4070
|
+
const capabilities = safeObject(initParams.capabilities);
|
|
4071
|
+
return Object.prototype.hasOwnProperty.call(capabilities, "roots");
|
|
4072
|
+
}
|
|
4051
4073
|
|
|
4052
4074
|
/**
|
|
4053
4075
|
* Probe the MCP client for workspace roots via the standard roots/list request.
|
|
@@ -4093,7 +4115,7 @@ async function runProxy(flags) {
|
|
|
4093
4115
|
} catch { /* best-effort */ }
|
|
4094
4116
|
}
|
|
4095
4117
|
}, 10000);
|
|
4096
|
-
|
|
4118
|
+
writeProxyJson({ jsonrpc: "2.0", id, method: "roots/list" });
|
|
4097
4119
|
}
|
|
4098
4120
|
|
|
4099
4121
|
const handleIncomingMessage = async (lineRaw) => {
|
|
@@ -4102,7 +4124,7 @@ async function runProxy(flags) {
|
|
|
4102
4124
|
|
|
4103
4125
|
const requestObj = tryJsonParse(line);
|
|
4104
4126
|
if (requestObj == null) {
|
|
4105
|
-
|
|
4127
|
+
writeProxyJson(jsonRpcError(null, -32700, "parse error"));
|
|
4106
4128
|
return;
|
|
4107
4129
|
}
|
|
4108
4130
|
|
|
@@ -4141,16 +4163,14 @@ async function runProxy(flags) {
|
|
|
4141
4163
|
? `; expired token ignored: ${resolved.expired.source}${resolved.expired.expires ? ` (${resolved.expired.expires})` : ""}`
|
|
4142
4164
|
: "";
|
|
4143
4165
|
const refreshHint = lastRefreshError ? `; refresh failed: ${lastRefreshError}` : "";
|
|
4144
|
-
|
|
4145
|
-
|
|
4146
|
-
|
|
4147
|
-
|
|
4148
|
-
|
|
4149
|
-
|
|
4150
|
-
|
|
4151
|
-
|
|
4152
|
-
),
|
|
4153
|
-
)}\n`,
|
|
4166
|
+
writeProxyJson(
|
|
4167
|
+
jsonRpcError(
|
|
4168
|
+
requestObj,
|
|
4169
|
+
-32001,
|
|
4170
|
+
`missing auth token (set METHEUS_TOKEN or run 'metheus-governance-mcp auth login --base-url ${normalizeSiteBaseURL(
|
|
4171
|
+
args.baseURL,
|
|
4172
|
+
)}'; login: ${helpURL}${expiredHint}${refreshHint})`,
|
|
4173
|
+
),
|
|
4154
4174
|
);
|
|
4155
4175
|
return;
|
|
4156
4176
|
}
|
|
@@ -4239,15 +4259,11 @@ async function runProxy(flags) {
|
|
|
4239
4259
|
}),
|
|
4240
4260
|
).trim();
|
|
4241
4261
|
if (!projectID) {
|
|
4242
|
-
|
|
4243
|
-
`${JSON.stringify(jsonRpcError(requestObj, -32001, "project_id is required (or set --project-id during setup)"))}\n`,
|
|
4244
|
-
);
|
|
4262
|
+
writeProxyJson(jsonRpcError(requestObj, -32001, "project_id is required (or set --project-id during setup)"));
|
|
4245
4263
|
return;
|
|
4246
4264
|
}
|
|
4247
4265
|
if (!isUUID(projectID)) {
|
|
4248
|
-
|
|
4249
|
-
`${JSON.stringify(jsonRpcError(requestObj, -32001, "project_id must be a valid UUID"))}\n`,
|
|
4250
|
-
);
|
|
4266
|
+
writeProxyJson(jsonRpcError(requestObj, -32001, "project_id must be a valid UUID"));
|
|
4251
4267
|
return;
|
|
4252
4268
|
}
|
|
4253
4269
|
try {
|
|
@@ -4274,23 +4290,19 @@ async function runProxy(flags) {
|
|
|
4274
4290
|
workspaceSignalTrusted,
|
|
4275
4291
|
});
|
|
4276
4292
|
const text = buildProjectSummaryText(summary);
|
|
4277
|
-
|
|
4278
|
-
|
|
4279
|
-
|
|
4280
|
-
|
|
4281
|
-
|
|
4282
|
-
|
|
4283
|
-
|
|
4284
|
-
|
|
4285
|
-
|
|
4286
|
-
|
|
4287
|
-
}),
|
|
4288
|
-
)}\n`,
|
|
4293
|
+
writeProxyJson(
|
|
4294
|
+
jsonRpcResult(requestObj, {
|
|
4295
|
+
content: [
|
|
4296
|
+
{
|
|
4297
|
+
type: "text",
|
|
4298
|
+
text,
|
|
4299
|
+
},
|
|
4300
|
+
],
|
|
4301
|
+
structuredContent: summary,
|
|
4302
|
+
}),
|
|
4289
4303
|
);
|
|
4290
4304
|
} catch (err) {
|
|
4291
|
-
|
|
4292
|
-
`${JSON.stringify(jsonRpcError(requestObj, -32001, String(err?.message || err)))}\n`,
|
|
4293
|
-
);
|
|
4305
|
+
writeProxyJson(jsonRpcError(requestObj, -32001, String(err?.message || err)));
|
|
4294
4306
|
}
|
|
4295
4307
|
return;
|
|
4296
4308
|
}
|
|
@@ -4303,15 +4315,11 @@ async function runProxy(flags) {
|
|
|
4303
4315
|
}),
|
|
4304
4316
|
).trim();
|
|
4305
4317
|
if (!projectID) {
|
|
4306
|
-
|
|
4307
|
-
`${JSON.stringify(jsonRpcError(requestObj, -32001, "project_id is required (or set --project-id during setup)"))}\n`,
|
|
4308
|
-
);
|
|
4318
|
+
writeProxyJson(jsonRpcError(requestObj, -32001, "project_id is required (or set --project-id during setup)"));
|
|
4309
4319
|
return;
|
|
4310
4320
|
}
|
|
4311
4321
|
if (!isUUID(projectID)) {
|
|
4312
|
-
|
|
4313
|
-
`${JSON.stringify(jsonRpcError(requestObj, -32001, "project_id must be a valid UUID"))}\n`,
|
|
4314
|
-
);
|
|
4322
|
+
writeProxyJson(jsonRpcError(requestObj, -32001, "project_id must be a valid UUID"));
|
|
4315
4323
|
return;
|
|
4316
4324
|
}
|
|
4317
4325
|
|
|
@@ -4320,14 +4328,12 @@ async function runProxy(flags) {
|
|
|
4320
4328
|
const statusRaw = String(toolArgs.status || "").trim().toLowerCase();
|
|
4321
4329
|
const statusFilter = normalizeMergeStatusFilter(statusRaw);
|
|
4322
4330
|
if (statusRaw && !statusFilter) {
|
|
4323
|
-
|
|
4324
|
-
|
|
4325
|
-
|
|
4326
|
-
|
|
4327
|
-
|
|
4328
|
-
|
|
4329
|
-
),
|
|
4330
|
-
)}\n`,
|
|
4331
|
+
writeProxyJson(
|
|
4332
|
+
jsonRpcError(
|
|
4333
|
+
requestObj,
|
|
4334
|
+
-32001,
|
|
4335
|
+
"status must be one of: open, review, approved, rejected, merged, closed, all",
|
|
4336
|
+
),
|
|
4331
4337
|
);
|
|
4332
4338
|
return;
|
|
4333
4339
|
}
|
|
@@ -4342,13 +4348,11 @@ async function runProxy(flags) {
|
|
|
4342
4348
|
workspaceDir: requestWorkspaceDir,
|
|
4343
4349
|
});
|
|
4344
4350
|
const text = buildCtxpackMergeBriefText(brief);
|
|
4345
|
-
|
|
4346
|
-
|
|
4347
|
-
|
|
4348
|
-
|
|
4349
|
-
|
|
4350
|
-
}),
|
|
4351
|
-
)}\n`,
|
|
4351
|
+
writeProxyJson(
|
|
4352
|
+
jsonRpcResult(requestObj, {
|
|
4353
|
+
content: [{ type: "text", text }],
|
|
4354
|
+
structuredContent: brief,
|
|
4355
|
+
}),
|
|
4352
4356
|
);
|
|
4353
4357
|
return;
|
|
4354
4358
|
}
|
|
@@ -4360,16 +4364,12 @@ async function runProxy(flags) {
|
|
|
4360
4364
|
toolArgs.owner_confirmation || toolArgs.ownerConfirmation || "",
|
|
4361
4365
|
).trim();
|
|
4362
4366
|
if (!mergeRequestID || !isUUID(mergeRequestID)) {
|
|
4363
|
-
|
|
4364
|
-
`${JSON.stringify(jsonRpcError(requestObj, -32001, "merge_request_id must be a valid UUID"))}\n`,
|
|
4365
|
-
);
|
|
4367
|
+
writeProxyJson(jsonRpcError(requestObj, -32001, "merge_request_id must be a valid UUID"));
|
|
4366
4368
|
return;
|
|
4367
4369
|
}
|
|
4368
4370
|
if (!action) {
|
|
4369
|
-
|
|
4370
|
-
|
|
4371
|
-
jsonRpcError(requestObj, -32001, "action must be one of: review, approve, reject, close, merge"),
|
|
4372
|
-
)}\n`,
|
|
4371
|
+
writeProxyJson(
|
|
4372
|
+
jsonRpcError(requestObj, -32001, "action must be one of: review, approve, reject, close, merge"),
|
|
4373
4373
|
);
|
|
4374
4374
|
return;
|
|
4375
4375
|
}
|
|
@@ -4384,20 +4384,16 @@ async function runProxy(flags) {
|
|
|
4384
4384
|
workspaceDir: requestWorkspaceDir,
|
|
4385
4385
|
});
|
|
4386
4386
|
const text = buildCtxpackMergeExecuteText(result);
|
|
4387
|
-
|
|
4388
|
-
|
|
4389
|
-
|
|
4390
|
-
|
|
4391
|
-
|
|
4392
|
-
}),
|
|
4393
|
-
)}\n`,
|
|
4387
|
+
writeProxyJson(
|
|
4388
|
+
jsonRpcResult(requestObj, {
|
|
4389
|
+
content: [{ type: "text", text }],
|
|
4390
|
+
structuredContent: result,
|
|
4391
|
+
}),
|
|
4394
4392
|
);
|
|
4395
4393
|
return;
|
|
4396
4394
|
}
|
|
4397
4395
|
} catch (err) {
|
|
4398
|
-
|
|
4399
|
-
`${JSON.stringify(jsonRpcError(requestObj, -32001, String(err?.message || err)))}\n`,
|
|
4400
|
-
);
|
|
4396
|
+
writeProxyJson(jsonRpcError(requestObj, -32001, String(err?.message || err)));
|
|
4401
4397
|
return;
|
|
4402
4398
|
}
|
|
4403
4399
|
}
|
|
@@ -4429,7 +4425,7 @@ async function runProxy(flags) {
|
|
|
4429
4425
|
requestWorkspaceDir,
|
|
4430
4426
|
workspaceSignalTrusted,
|
|
4431
4427
|
);
|
|
4432
|
-
|
|
4428
|
+
writeProxyJson(patched);
|
|
4433
4429
|
return;
|
|
4434
4430
|
}
|
|
4435
4431
|
if (isJsonRpcMethod(requestObj, "tools/list")) {
|
|
@@ -4445,10 +4441,12 @@ async function runProxy(flags) {
|
|
|
4445
4441
|
`[${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
4442
|
);
|
|
4447
4443
|
} catch { /* best-effort */ }
|
|
4448
|
-
// Probe
|
|
4449
|
-
//
|
|
4450
|
-
|
|
4451
|
-
|
|
4444
|
+
// Probe roots/list only when client advertised roots capability.
|
|
4445
|
+
// Some MCP clients reject unsolicited server requests and then hide tools.
|
|
4446
|
+
if (clientSupportsRootsList(requestObj?.params)) {
|
|
4447
|
+
// Use setImmediate so the initialize response is flushed first.
|
|
4448
|
+
setImmediate(() => sendRootsListProbe());
|
|
4449
|
+
}
|
|
4452
4450
|
} else if (isJsonRpcMethod(requestObj, "tools/call") && toolName === "workitem.list") {
|
|
4453
4451
|
patched = await appendWorkitemListHints(patched, args, toolArgs, token, workspaceSignalTrusted);
|
|
4454
4452
|
} else if (isJsonRpcMethod(requestObj, "tools/call") && toolName === "ctxpack.ensure") {
|
|
@@ -4463,21 +4461,17 @@ async function runProxy(flags) {
|
|
|
4463
4461
|
if (autoSyncSummary) {
|
|
4464
4462
|
patched = appendAutoCtxpackSyncHint(patched, autoSyncSummary);
|
|
4465
4463
|
}
|
|
4466
|
-
|
|
4464
|
+
writeProxyJson(patched);
|
|
4467
4465
|
return;
|
|
4468
4466
|
}
|
|
4469
|
-
|
|
4467
|
+
writeProxyMessage(responseText);
|
|
4470
4468
|
return;
|
|
4471
4469
|
}
|
|
4472
4470
|
if (Object.prototype.hasOwnProperty.call(requestObj, "id")) {
|
|
4473
|
-
|
|
4474
|
-
`${JSON.stringify(jsonRpcError(requestObj, -32001, "empty gateway response"))}\n`,
|
|
4475
|
-
);
|
|
4471
|
+
writeProxyJson(jsonRpcError(requestObj, -32001, "empty gateway response"));
|
|
4476
4472
|
}
|
|
4477
4473
|
} catch (err) {
|
|
4478
|
-
|
|
4479
|
-
`${JSON.stringify(jsonRpcError(requestObj, -32001, String(err?.message || err)))}\n`,
|
|
4480
|
-
);
|
|
4474
|
+
writeProxyJson(jsonRpcError(requestObj, -32001, String(err?.message || err)));
|
|
4481
4475
|
}
|
|
4482
4476
|
};
|
|
4483
4477
|
|
|
@@ -4576,6 +4570,7 @@ async function runProxy(flags) {
|
|
|
4576
4570
|
if (pendingInputBuffer.length === 0) break;
|
|
4577
4571
|
|
|
4578
4572
|
if (startsWithContentLengthHeader(pendingInputBuffer)) {
|
|
4573
|
+
stdoutEnvelopeMode = "framed";
|
|
4579
4574
|
const framed = extractFramedMessage(pendingInputBuffer);
|
|
4580
4575
|
if (!framed) break;
|
|
4581
4576
|
pendingInputBuffer = framed.remaining;
|