@rigour-labs/mcp 2.11.0 → 2.12.0
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/dist/index.js +92 -0
- package/package.json +3 -2
- package/src/index.ts +96 -0
package/dist/index.js
CHANGED
|
@@ -243,6 +243,28 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
243
243
|
required: ["cwd"],
|
|
244
244
|
},
|
|
245
245
|
},
|
|
246
|
+
{
|
|
247
|
+
name: "rigour_run",
|
|
248
|
+
description: "Execute a command under Rigour supervision. This tool can be INTERCEPTED and ARBITRATED by the Governance Studio.",
|
|
249
|
+
inputSchema: {
|
|
250
|
+
type: "object",
|
|
251
|
+
properties: {
|
|
252
|
+
cwd: {
|
|
253
|
+
type: "string",
|
|
254
|
+
description: "Absolute path to the project root.",
|
|
255
|
+
},
|
|
256
|
+
command: {
|
|
257
|
+
type: "string",
|
|
258
|
+
description: "The command to run (e.g., 'npm test', 'pytest').",
|
|
259
|
+
},
|
|
260
|
+
silent: {
|
|
261
|
+
type: "boolean",
|
|
262
|
+
description: "If true, hides the command output from the agent.",
|
|
263
|
+
}
|
|
264
|
+
},
|
|
265
|
+
required: ["cwd", "command"],
|
|
266
|
+
},
|
|
267
|
+
}
|
|
246
268
|
],
|
|
247
269
|
};
|
|
248
270
|
});
|
|
@@ -560,6 +582,76 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
560
582
|
};
|
|
561
583
|
break;
|
|
562
584
|
}
|
|
585
|
+
case "rigour_run": {
|
|
586
|
+
const { command } = args;
|
|
587
|
+
// 1. Log Interceptable Event
|
|
588
|
+
await logStudioEvent(cwd, {
|
|
589
|
+
type: "interception_requested",
|
|
590
|
+
requestId: requestId,
|
|
591
|
+
tool: "rigour_run",
|
|
592
|
+
command
|
|
593
|
+
});
|
|
594
|
+
// 2. Poll for Human Arbitration (Max 60s wait for this demo/test)
|
|
595
|
+
// In production, this would be a blocking call wait
|
|
596
|
+
console.error(`[RIGOUR] Waiting for human arbitration for command: ${command}`);
|
|
597
|
+
const pollArbitration = async (rid, timeout) => {
|
|
598
|
+
const start = Date.now();
|
|
599
|
+
const eventsPath = path.join(cwd, '.rigour/events.jsonl');
|
|
600
|
+
while (Date.now() - start < timeout) {
|
|
601
|
+
if (await fs.pathExists(eventsPath)) {
|
|
602
|
+
const content = await fs.readFile(eventsPath, 'utf-8');
|
|
603
|
+
const lines = content.split('\n').filter(l => l.trim());
|
|
604
|
+
for (const line of lines.reverse()) {
|
|
605
|
+
const event = JSON.parse(line);
|
|
606
|
+
if (event.tool === 'human_arbitration' && event.requestId === rid) {
|
|
607
|
+
return event.decision;
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
await new Promise(r => setTimeout(r, 1000));
|
|
612
|
+
}
|
|
613
|
+
return "approve"; // Default to auto-approve if no human response (for non-blocking feel)
|
|
614
|
+
};
|
|
615
|
+
const decision = await pollArbitration(requestId, 60000);
|
|
616
|
+
if (decision === 'reject') {
|
|
617
|
+
result = {
|
|
618
|
+
content: [
|
|
619
|
+
{
|
|
620
|
+
type: "text",
|
|
621
|
+
text: `❌ COMMAND REJECTED BY GOVERNOR: The execution of "${command}" was blocked by a human operator in the Governance Studio.`,
|
|
622
|
+
},
|
|
623
|
+
],
|
|
624
|
+
isError: true
|
|
625
|
+
};
|
|
626
|
+
}
|
|
627
|
+
else {
|
|
628
|
+
// Execute
|
|
629
|
+
const { execa } = await import("execa");
|
|
630
|
+
try {
|
|
631
|
+
const { stdout, stderr } = await execa(command, { shell: true, cwd });
|
|
632
|
+
result = {
|
|
633
|
+
content: [
|
|
634
|
+
{
|
|
635
|
+
type: "text",
|
|
636
|
+
text: `✅ COMMAND EXECUTED (Approved by Governor):\n\nSTDOUT:\n${stdout}\n\nSTDERR:\n${stderr}`,
|
|
637
|
+
},
|
|
638
|
+
],
|
|
639
|
+
};
|
|
640
|
+
}
|
|
641
|
+
catch (e) {
|
|
642
|
+
result = {
|
|
643
|
+
content: [
|
|
644
|
+
{
|
|
645
|
+
type: "text",
|
|
646
|
+
text: `❌ COMMAND FAILED:\n\n${e.message}`,
|
|
647
|
+
},
|
|
648
|
+
],
|
|
649
|
+
isError: true
|
|
650
|
+
};
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
break;
|
|
654
|
+
}
|
|
563
655
|
default:
|
|
564
656
|
throw new Error(`Unknown tool: ${name}`);
|
|
565
657
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rigour-labs/mcp",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.12.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"mcpName": "io.github.rigour-labs/rigour",
|
|
6
6
|
"description": "Quality gates for AI-generated code. Forces AI agents to meet strict engineering standards with PASS/FAIL enforcement.",
|
|
@@ -17,9 +17,10 @@
|
|
|
17
17
|
},
|
|
18
18
|
"dependencies": {
|
|
19
19
|
"@modelcontextprotocol/sdk": "^1.25.2",
|
|
20
|
+
"execa": "^8.0.1",
|
|
20
21
|
"fs-extra": "^11.2.0",
|
|
21
22
|
"yaml": "^2.8.2",
|
|
22
|
-
"@rigour-labs/core": "2.
|
|
23
|
+
"@rigour-labs/core": "2.12.0"
|
|
23
24
|
},
|
|
24
25
|
"devDependencies": {
|
|
25
26
|
"@types/node": "^25.0.3"
|
package/src/index.ts
CHANGED
|
@@ -270,6 +270,28 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
270
270
|
required: ["cwd"],
|
|
271
271
|
},
|
|
272
272
|
},
|
|
273
|
+
{
|
|
274
|
+
name: "rigour_run",
|
|
275
|
+
description: "Execute a command under Rigour supervision. This tool can be INTERCEPTED and ARBITRATED by the Governance Studio.",
|
|
276
|
+
inputSchema: {
|
|
277
|
+
type: "object",
|
|
278
|
+
properties: {
|
|
279
|
+
cwd: {
|
|
280
|
+
type: "string",
|
|
281
|
+
description: "Absolute path to the project root.",
|
|
282
|
+
},
|
|
283
|
+
command: {
|
|
284
|
+
type: "string",
|
|
285
|
+
description: "The command to run (e.g., 'npm test', 'pytest').",
|
|
286
|
+
},
|
|
287
|
+
silent: {
|
|
288
|
+
type: "boolean",
|
|
289
|
+
description: "If true, hides the command output from the agent.",
|
|
290
|
+
}
|
|
291
|
+
},
|
|
292
|
+
required: ["cwd", "command"],
|
|
293
|
+
},
|
|
294
|
+
}
|
|
273
295
|
],
|
|
274
296
|
};
|
|
275
297
|
});
|
|
@@ -612,6 +634,80 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
612
634
|
break;
|
|
613
635
|
}
|
|
614
636
|
|
|
637
|
+
case "rigour_run": {
|
|
638
|
+
const { command } = args as any;
|
|
639
|
+
|
|
640
|
+
// 1. Log Interceptable Event
|
|
641
|
+
await logStudioEvent(cwd, {
|
|
642
|
+
type: "interception_requested",
|
|
643
|
+
requestId: requestId,
|
|
644
|
+
tool: "rigour_run",
|
|
645
|
+
command
|
|
646
|
+
});
|
|
647
|
+
|
|
648
|
+
// 2. Poll for Human Arbitration (Max 60s wait for this demo/test)
|
|
649
|
+
// In production, this would be a blocking call wait
|
|
650
|
+
console.error(`[RIGOUR] Waiting for human arbitration for command: ${command}`);
|
|
651
|
+
|
|
652
|
+
const pollArbitration = async (rid: string, timeout: number): Promise<string | null> => {
|
|
653
|
+
const start = Date.now();
|
|
654
|
+
const eventsPath = path.join(cwd, '.rigour/events.jsonl');
|
|
655
|
+
while (Date.now() - start < timeout) {
|
|
656
|
+
if (await fs.pathExists(eventsPath)) {
|
|
657
|
+
const content = await fs.readFile(eventsPath, 'utf-8');
|
|
658
|
+
const lines = content.split('\n').filter(l => l.trim());
|
|
659
|
+
for (const line of lines.reverse()) {
|
|
660
|
+
const event = JSON.parse(line);
|
|
661
|
+
if (event.tool === 'human_arbitration' && event.requestId === rid) {
|
|
662
|
+
return event.decision;
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
await new Promise(r => setTimeout(r, 1000));
|
|
667
|
+
}
|
|
668
|
+
return "approve"; // Default to auto-approve if no human response (for non-blocking feel)
|
|
669
|
+
};
|
|
670
|
+
|
|
671
|
+
const decision = await pollArbitration(requestId, 60000);
|
|
672
|
+
|
|
673
|
+
if (decision === 'reject') {
|
|
674
|
+
result = {
|
|
675
|
+
content: [
|
|
676
|
+
{
|
|
677
|
+
type: "text",
|
|
678
|
+
text: `❌ COMMAND REJECTED BY GOVERNOR: The execution of "${command}" was blocked by a human operator in the Governance Studio.`,
|
|
679
|
+
},
|
|
680
|
+
],
|
|
681
|
+
isError: true
|
|
682
|
+
};
|
|
683
|
+
} else {
|
|
684
|
+
// Execute
|
|
685
|
+
const { execa } = await import("execa");
|
|
686
|
+
try {
|
|
687
|
+
const { stdout, stderr } = await execa(command, { shell: true, cwd });
|
|
688
|
+
result = {
|
|
689
|
+
content: [
|
|
690
|
+
{
|
|
691
|
+
type: "text",
|
|
692
|
+
text: `✅ COMMAND EXECUTED (Approved by Governor):\n\nSTDOUT:\n${stdout}\n\nSTDERR:\n${stderr}`,
|
|
693
|
+
},
|
|
694
|
+
],
|
|
695
|
+
};
|
|
696
|
+
} catch (e: any) {
|
|
697
|
+
result = {
|
|
698
|
+
content: [
|
|
699
|
+
{
|
|
700
|
+
type: "text",
|
|
701
|
+
text: `❌ COMMAND FAILED:\n\n${e.message}`,
|
|
702
|
+
},
|
|
703
|
+
],
|
|
704
|
+
isError: true
|
|
705
|
+
};
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
break;
|
|
709
|
+
}
|
|
710
|
+
|
|
615
711
|
default:
|
|
616
712
|
throw new Error(`Unknown tool: ${name}`);
|
|
617
713
|
}
|