@rigour-labs/mcp 5.2.3 → 5.2.5
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/dashboard/html.d.ts +15 -0
- package/dist/dashboard/html.js +182 -0
- package/dist/dashboard/index.d.ts +4 -0
- package/dist/dashboard/index.js +16 -0
- package/dist/dashboard/state.d.ts +26 -0
- package/dist/dashboard/state.js +44 -0
- package/dist/index.js +52 -2
- package/dist/tools/deep-handlers.js +4 -0
- package/dist/tools/definitions.js +3 -3
- package/dist/tools/execution-handlers.js +5 -0
- package/dist/tools/pattern-handlers.js +3 -0
- package/dist/tools/quality-handlers.js +36 -2
- package/dist/tools/review-handler.js +2 -0
- package/dist/utils/notifications.d.ts +18 -0
- package/dist/utils/notifications.js +19 -0
- package/package.json +24 -10
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dashboard HTML — Self-contained MCP App UI
|
|
3
|
+
*
|
|
4
|
+
* Served as a ui:// resource. Renders in a sandboxed iframe
|
|
5
|
+
* inside Claude Desktop, VS Code Copilot, ChatGPT, Goose.
|
|
6
|
+
*
|
|
7
|
+
* Features:
|
|
8
|
+
* - SVG circular score gauge (color-coded)
|
|
9
|
+
* - Scrolling activity timeline
|
|
10
|
+
* - Severity breakdown bar
|
|
11
|
+
* - Brain learning indicator
|
|
12
|
+
*
|
|
13
|
+
* No external dependencies. Initial state injected via __INITIAL_STATE__.
|
|
14
|
+
*/
|
|
15
|
+
export declare const DASHBOARD_HTML = "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"UTF-8\">\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n<title>Rigour Governance</title>\n<style>\n*{margin:0;padding:0;box-sizing:border-box}\nbody{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;background:#0d1117;color:#c9d1d9;padding:16px;font-size:13px;line-height:1.5}\n.header{display:flex;align-items:center;gap:8px;margin-bottom:16px}\n.header h1{font-size:15px;font-weight:600;color:#f0f6fc}\n.header .badge{font-size:11px;padding:2px 8px;border-radius:12px;font-weight:500}\n.badge-pass{background:#1a7f37;color:#fff}\n.badge-fail{background:#da3633;color:#fff}\n.badge-scanning{background:#9e6a03;color:#fff}\n.badge-idle{background:#30363d;color:#8b949e}\n.top{display:flex;gap:16px;margin-bottom:16px}\n.gauge-wrap{flex:0 0 100px;text-align:center}\n.gauge-label{font-size:11px;color:#8b949e;margin-top:4px}\n.severity-wrap{flex:1;display:flex;flex-direction:column;justify-content:center;gap:6px}\n.sev-row{display:flex;align-items:center;gap:6px;font-size:12px}\n.sev-dot{width:8px;height:8px;border-radius:50%;flex-shrink:0}\n.sev-critical{background:#da3633}\n.sev-high{background:#db6d28}\n.sev-medium{background:#9e6a03}\n.sev-low{background:#388bfd}\n.sev-info{background:#8b949e}\n.sev-count{color:#f0f6fc;font-weight:600;min-width:18px}\n.section-title{font-size:11px;color:#8b949e;text-transform:uppercase;letter-spacing:.5px;margin-bottom:8px;font-weight:600}\n.timeline{max-height:200px;overflow-y:auto;margin-bottom:16px}\n.timeline::-webkit-scrollbar{width:4px}\n.timeline::-webkit-scrollbar-thumb{background:#30363d;border-radius:2px}\n.tl-entry{display:flex;gap:8px;padding:4px 0;border-bottom:1px solid #21262d;font-size:12px}\n.tl-time{color:#8b949e;flex:0 0 55px;font-family:'SF Mono',Monaco,monospace;font-size:11px}\n.tl-tool{color:#58a6ff;flex:0 0 auto}\n.tl-arrow{color:#484f58}\n.tl-detail{color:#c9d1d9;flex:1}\n.tl-status-success .tl-detail{color:#3fb950}\n.tl-status-error .tl-detail{color:#da3633}\n.tl-status-warning .tl-detail{color:#d29922}\n.brain{display:flex;align-items:center;gap:8px;padding:8px 12px;background:#161b22;border:1px solid #30363d;border-radius:6px;font-size:12px}\n.brain-icon{font-size:16px}\n.brain-text{color:#c9d1d9}\n.brain-trend{color:#3fb950;font-weight:600}\n.brain-trend.down{color:#da3633}\n.empty{text-align:center;padding:32px 16px;color:#484f58;font-size:12px}\n</style>\n</head>\n<body>\n<div class=\"header\">\n <h1>Rigour Governance</h1>\n <span class=\"badge badge-idle\" id=\"statusBadge\">idle</span>\n</div>\n\n<div class=\"top\">\n <div class=\"gauge-wrap\">\n <svg viewBox=\"0 0 100 100\" width=\"90\" height=\"90\">\n <circle cx=\"50\" cy=\"50\" r=\"42\" fill=\"none\" stroke=\"#21262d\" stroke-width=\"8\"/>\n <circle cx=\"50\" cy=\"50\" r=\"42\" fill=\"none\" stroke=\"#30363d\" stroke-width=\"8\"\n stroke-dasharray=\"264\" stroke-dashoffset=\"264\" stroke-linecap=\"round\"\n transform=\"rotate(-90 50 50)\" id=\"gaugeArc\"/>\n <text x=\"50\" y=\"46\" text-anchor=\"middle\" fill=\"#f0f6fc\" font-size=\"22\" font-weight=\"700\" id=\"gaugeText\">--</text>\n <text x=\"50\" y=\"62\" text-anchor=\"middle\" fill=\"#8b949e\" font-size=\"9\">SCORE</text>\n </svg>\n </div>\n <div class=\"severity-wrap\" id=\"severityWrap\">\n <div class=\"sev-row\"><span class=\"sev-dot sev-critical\"></span><span class=\"sev-count\" id=\"sevCritical\">0</span>critical</div>\n <div class=\"sev-row\"><span class=\"sev-dot sev-high\"></span><span class=\"sev-count\" id=\"sevHigh\">0</span>high</div>\n <div class=\"sev-row\"><span class=\"sev-dot sev-medium\"></span><span class=\"sev-count\" id=\"sevMedium\">0</span>medium</div>\n <div class=\"sev-row\"><span class=\"sev-dot sev-low\"></span><span class=\"sev-count\" id=\"sevLow\">0</span>low</div>\n </div>\n</div>\n\n<div class=\"section-title\">Activity</div>\n<div class=\"timeline\" id=\"timeline\">\n <div class=\"empty\">Waiting for agent activity...</div>\n</div>\n\n<div class=\"brain\" id=\"brainSection\">\n <span class=\"brain-icon\">🧠</span>\n <span class=\"brain-text\" id=\"brainText\">Brain: waiting for data</span>\n <span class=\"brain-trend\" id=\"brainTrend\"></span>\n</div>\n\n<script>\nconst state = JSON.parse('__INITIAL_STATE__');\n\nfunction scoreColor(s) {\n if (s === null) return '#30363d';\n if (s < 50) return '#da3633';\n if (s < 80) return '#d29922';\n return '#3fb950';\n}\n\nfunction updateGauge(score) {\n const arc = document.getElementById('gaugeArc');\n const text = document.getElementById('gaugeText');\n if (score === null) {\n arc.setAttribute('stroke-dashoffset', '264');\n arc.setAttribute('stroke', '#30363d');\n text.textContent = '--';\n return;\n }\n const offset = 264 - (264 * Math.min(score, 100) / 100);\n arc.setAttribute('stroke-dashoffset', String(offset));\n arc.setAttribute('stroke', scoreColor(score));\n text.textContent = String(Math.round(score));\n}\n\nfunction updateBadge(status) {\n const badge = document.getElementById('statusBadge');\n badge.className = 'badge badge-' + (status || 'idle');\n const labels = {pass:'PASS',fail:'FAIL',scanning:'SCANNING'};\n badge.textContent = labels[status] || 'IDLE';\n}\n\nfunction updateSeverity(sev) {\n document.getElementById('sevCritical').textContent = sev.critical || 0;\n document.getElementById('sevHigh').textContent = sev.high || 0;\n document.getElementById('sevMedium').textContent = sev.medium || 0;\n document.getElementById('sevLow').textContent = sev.low || 0;\n}\n\nfunction formatTime(iso) {\n const d = new Date(iso);\n return d.toLocaleTimeString('en-US', {hour12:false,hour:'2-digit',minute:'2-digit',second:'2-digit'});\n}\n\nfunction renderTimeline(entries) {\n const el = document.getElementById('timeline');\n if (!entries.length) {\n el.innerHTML = '<div class=\"empty\">Waiting for agent activity...</div>';\n return;\n }\n el.innerHTML = entries.map(e => {\n const cls = e.status === 'success' ? 'tl-status-success' : e.status === 'error' ? 'tl-status-error' : 'tl-status-warning';\n return '<div class=\"tl-entry ' + cls + '\">' +\n '<span class=\"tl-time\">' + formatTime(e.timestamp) + '</span>' +\n '<span class=\"tl-tool\">' + e.tool + '</span>' +\n '<span class=\"tl-arrow\">\u2192</span>' +\n '<span class=\"tl-detail\">' + e.details + '</span></div>';\n }).join('');\n el.scrollTop = el.scrollHeight;\n}\n\nfunction updateBrain(patterns, trend) {\n const text = document.getElementById('brainText');\n const trendEl = document.getElementById('brainTrend');\n text.textContent = 'Brain: ' + patterns + ' patterns learned';\n if (trend) {\n const arrows = {improving:'\u2191',stable:'\u2194',declining:'\u2193'};\n trendEl.textContent = (arrows[trend] || '') + ' ' + trend;\n trendEl.className = 'brain-trend' + (trend === 'declining' ? ' down' : '');\n }\n}\n\nfunction render() {\n updateGauge(state.currentScore);\n updateBadge(state.status);\n updateSeverity(state.severityBreakdown || {});\n renderTimeline(state.timeline || []);\n updateBrain(state.brainPatterns || 0, state.brainTrend);\n}\n\nrender();\n</script>\n</body>\n</html>";
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dashboard HTML — Self-contained MCP App UI
|
|
3
|
+
*
|
|
4
|
+
* Served as a ui:// resource. Renders in a sandboxed iframe
|
|
5
|
+
* inside Claude Desktop, VS Code Copilot, ChatGPT, Goose.
|
|
6
|
+
*
|
|
7
|
+
* Features:
|
|
8
|
+
* - SVG circular score gauge (color-coded)
|
|
9
|
+
* - Scrolling activity timeline
|
|
10
|
+
* - Severity breakdown bar
|
|
11
|
+
* - Brain learning indicator
|
|
12
|
+
*
|
|
13
|
+
* No external dependencies. Initial state injected via __INITIAL_STATE__.
|
|
14
|
+
*/
|
|
15
|
+
export const DASHBOARD_HTML = `<!DOCTYPE html>
|
|
16
|
+
<html lang="en">
|
|
17
|
+
<head>
|
|
18
|
+
<meta charset="UTF-8">
|
|
19
|
+
<meta name="viewport" content="width=device-width,initial-scale=1">
|
|
20
|
+
<title>Rigour Governance</title>
|
|
21
|
+
<style>
|
|
22
|
+
*{margin:0;padding:0;box-sizing:border-box}
|
|
23
|
+
body{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;background:#0d1117;color:#c9d1d9;padding:16px;font-size:13px;line-height:1.5}
|
|
24
|
+
.header{display:flex;align-items:center;gap:8px;margin-bottom:16px}
|
|
25
|
+
.header h1{font-size:15px;font-weight:600;color:#f0f6fc}
|
|
26
|
+
.header .badge{font-size:11px;padding:2px 8px;border-radius:12px;font-weight:500}
|
|
27
|
+
.badge-pass{background:#1a7f37;color:#fff}
|
|
28
|
+
.badge-fail{background:#da3633;color:#fff}
|
|
29
|
+
.badge-scanning{background:#9e6a03;color:#fff}
|
|
30
|
+
.badge-idle{background:#30363d;color:#8b949e}
|
|
31
|
+
.top{display:flex;gap:16px;margin-bottom:16px}
|
|
32
|
+
.gauge-wrap{flex:0 0 100px;text-align:center}
|
|
33
|
+
.gauge-label{font-size:11px;color:#8b949e;margin-top:4px}
|
|
34
|
+
.severity-wrap{flex:1;display:flex;flex-direction:column;justify-content:center;gap:6px}
|
|
35
|
+
.sev-row{display:flex;align-items:center;gap:6px;font-size:12px}
|
|
36
|
+
.sev-dot{width:8px;height:8px;border-radius:50%;flex-shrink:0}
|
|
37
|
+
.sev-critical{background:#da3633}
|
|
38
|
+
.sev-high{background:#db6d28}
|
|
39
|
+
.sev-medium{background:#9e6a03}
|
|
40
|
+
.sev-low{background:#388bfd}
|
|
41
|
+
.sev-info{background:#8b949e}
|
|
42
|
+
.sev-count{color:#f0f6fc;font-weight:600;min-width:18px}
|
|
43
|
+
.section-title{font-size:11px;color:#8b949e;text-transform:uppercase;letter-spacing:.5px;margin-bottom:8px;font-weight:600}
|
|
44
|
+
.timeline{max-height:200px;overflow-y:auto;margin-bottom:16px}
|
|
45
|
+
.timeline::-webkit-scrollbar{width:4px}
|
|
46
|
+
.timeline::-webkit-scrollbar-thumb{background:#30363d;border-radius:2px}
|
|
47
|
+
.tl-entry{display:flex;gap:8px;padding:4px 0;border-bottom:1px solid #21262d;font-size:12px}
|
|
48
|
+
.tl-time{color:#8b949e;flex:0 0 55px;font-family:'SF Mono',Monaco,monospace;font-size:11px}
|
|
49
|
+
.tl-tool{color:#58a6ff;flex:0 0 auto}
|
|
50
|
+
.tl-arrow{color:#484f58}
|
|
51
|
+
.tl-detail{color:#c9d1d9;flex:1}
|
|
52
|
+
.tl-status-success .tl-detail{color:#3fb950}
|
|
53
|
+
.tl-status-error .tl-detail{color:#da3633}
|
|
54
|
+
.tl-status-warning .tl-detail{color:#d29922}
|
|
55
|
+
.brain{display:flex;align-items:center;gap:8px;padding:8px 12px;background:#161b22;border:1px solid #30363d;border-radius:6px;font-size:12px}
|
|
56
|
+
.brain-icon{font-size:16px}
|
|
57
|
+
.brain-text{color:#c9d1d9}
|
|
58
|
+
.brain-trend{color:#3fb950;font-weight:600}
|
|
59
|
+
.brain-trend.down{color:#da3633}
|
|
60
|
+
.empty{text-align:center;padding:32px 16px;color:#484f58;font-size:12px}
|
|
61
|
+
</style>
|
|
62
|
+
</head>
|
|
63
|
+
<body>
|
|
64
|
+
<div class="header">
|
|
65
|
+
<h1>Rigour Governance</h1>
|
|
66
|
+
<span class="badge badge-idle" id="statusBadge">idle</span>
|
|
67
|
+
</div>
|
|
68
|
+
|
|
69
|
+
<div class="top">
|
|
70
|
+
<div class="gauge-wrap">
|
|
71
|
+
<svg viewBox="0 0 100 100" width="90" height="90">
|
|
72
|
+
<circle cx="50" cy="50" r="42" fill="none" stroke="#21262d" stroke-width="8"/>
|
|
73
|
+
<circle cx="50" cy="50" r="42" fill="none" stroke="#30363d" stroke-width="8"
|
|
74
|
+
stroke-dasharray="264" stroke-dashoffset="264" stroke-linecap="round"
|
|
75
|
+
transform="rotate(-90 50 50)" id="gaugeArc"/>
|
|
76
|
+
<text x="50" y="46" text-anchor="middle" fill="#f0f6fc" font-size="22" font-weight="700" id="gaugeText">--</text>
|
|
77
|
+
<text x="50" y="62" text-anchor="middle" fill="#8b949e" font-size="9">SCORE</text>
|
|
78
|
+
</svg>
|
|
79
|
+
</div>
|
|
80
|
+
<div class="severity-wrap" id="severityWrap">
|
|
81
|
+
<div class="sev-row"><span class="sev-dot sev-critical"></span><span class="sev-count" id="sevCritical">0</span>critical</div>
|
|
82
|
+
<div class="sev-row"><span class="sev-dot sev-high"></span><span class="sev-count" id="sevHigh">0</span>high</div>
|
|
83
|
+
<div class="sev-row"><span class="sev-dot sev-medium"></span><span class="sev-count" id="sevMedium">0</span>medium</div>
|
|
84
|
+
<div class="sev-row"><span class="sev-dot sev-low"></span><span class="sev-count" id="sevLow">0</span>low</div>
|
|
85
|
+
</div>
|
|
86
|
+
</div>
|
|
87
|
+
|
|
88
|
+
<div class="section-title">Activity</div>
|
|
89
|
+
<div class="timeline" id="timeline">
|
|
90
|
+
<div class="empty">Waiting for agent activity...</div>
|
|
91
|
+
</div>
|
|
92
|
+
|
|
93
|
+
<div class="brain" id="brainSection">
|
|
94
|
+
<span class="brain-icon">🧠</span>
|
|
95
|
+
<span class="brain-text" id="brainText">Brain: waiting for data</span>
|
|
96
|
+
<span class="brain-trend" id="brainTrend"></span>
|
|
97
|
+
</div>
|
|
98
|
+
|
|
99
|
+
<script>
|
|
100
|
+
const state = JSON.parse('__INITIAL_STATE__');
|
|
101
|
+
|
|
102
|
+
function scoreColor(s) {
|
|
103
|
+
if (s === null) return '#30363d';
|
|
104
|
+
if (s < 50) return '#da3633';
|
|
105
|
+
if (s < 80) return '#d29922';
|
|
106
|
+
return '#3fb950';
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function updateGauge(score) {
|
|
110
|
+
const arc = document.getElementById('gaugeArc');
|
|
111
|
+
const text = document.getElementById('gaugeText');
|
|
112
|
+
if (score === null) {
|
|
113
|
+
arc.setAttribute('stroke-dashoffset', '264');
|
|
114
|
+
arc.setAttribute('stroke', '#30363d');
|
|
115
|
+
text.textContent = '--';
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
const offset = 264 - (264 * Math.min(score, 100) / 100);
|
|
119
|
+
arc.setAttribute('stroke-dashoffset', String(offset));
|
|
120
|
+
arc.setAttribute('stroke', scoreColor(score));
|
|
121
|
+
text.textContent = String(Math.round(score));
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function updateBadge(status) {
|
|
125
|
+
const badge = document.getElementById('statusBadge');
|
|
126
|
+
badge.className = 'badge badge-' + (status || 'idle');
|
|
127
|
+
const labels = {pass:'PASS',fail:'FAIL',scanning:'SCANNING'};
|
|
128
|
+
badge.textContent = labels[status] || 'IDLE';
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function updateSeverity(sev) {
|
|
132
|
+
document.getElementById('sevCritical').textContent = sev.critical || 0;
|
|
133
|
+
document.getElementById('sevHigh').textContent = sev.high || 0;
|
|
134
|
+
document.getElementById('sevMedium').textContent = sev.medium || 0;
|
|
135
|
+
document.getElementById('sevLow').textContent = sev.low || 0;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function formatTime(iso) {
|
|
139
|
+
const d = new Date(iso);
|
|
140
|
+
return d.toLocaleTimeString('en-US', {hour12:false,hour:'2-digit',minute:'2-digit',second:'2-digit'});
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function renderTimeline(entries) {
|
|
144
|
+
const el = document.getElementById('timeline');
|
|
145
|
+
if (!entries.length) {
|
|
146
|
+
el.innerHTML = '<div class="empty">Waiting for agent activity...</div>';
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
el.innerHTML = entries.map(e => {
|
|
150
|
+
const cls = e.status === 'success' ? 'tl-status-success' : e.status === 'error' ? 'tl-status-error' : 'tl-status-warning';
|
|
151
|
+
return '<div class="tl-entry ' + cls + '">' +
|
|
152
|
+
'<span class="tl-time">' + formatTime(e.timestamp) + '</span>' +
|
|
153
|
+
'<span class="tl-tool">' + e.tool + '</span>' +
|
|
154
|
+
'<span class="tl-arrow">\u2192</span>' +
|
|
155
|
+
'<span class="tl-detail">' + e.details + '</span></div>';
|
|
156
|
+
}).join('');
|
|
157
|
+
el.scrollTop = el.scrollHeight;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function updateBrain(patterns, trend) {
|
|
161
|
+
const text = document.getElementById('brainText');
|
|
162
|
+
const trendEl = document.getElementById('brainTrend');
|
|
163
|
+
text.textContent = 'Brain: ' + patterns + ' patterns learned';
|
|
164
|
+
if (trend) {
|
|
165
|
+
const arrows = {improving:'\u2191',stable:'\u2194',declining:'\u2193'};
|
|
166
|
+
trendEl.textContent = (arrows[trend] || '') + ' ' + trend;
|
|
167
|
+
trendEl.className = 'brain-trend' + (trend === 'declining' ? ' down' : '');
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function render() {
|
|
172
|
+
updateGauge(state.currentScore);
|
|
173
|
+
updateBadge(state.status);
|
|
174
|
+
updateSeverity(state.severityBreakdown || {});
|
|
175
|
+
renderTimeline(state.timeline || []);
|
|
176
|
+
updateBrain(state.brainPatterns || 0, state.brainTrend);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
render();
|
|
180
|
+
</script>
|
|
181
|
+
</body>
|
|
182
|
+
</html>`;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export declare const DASHBOARD_URI = "ui://rigour/dashboard";
|
|
2
|
+
/** Get dashboard HTML with current state injected. */
|
|
3
|
+
export declare function getDashboardHtml(): string;
|
|
4
|
+
export { getState, pushTimelineEntry, updateScore, updateBrainStatus, setScanning } from './state.js';
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dashboard Resource Serving
|
|
3
|
+
*
|
|
4
|
+
* Serves the MCP App dashboard as a ui:// resource.
|
|
5
|
+
* Injects current DashboardState into the HTML on each fetch
|
|
6
|
+
* so the client always gets the latest governance snapshot.
|
|
7
|
+
*/
|
|
8
|
+
import { DASHBOARD_HTML } from './html.js';
|
|
9
|
+
import { getState } from './state.js';
|
|
10
|
+
export const DASHBOARD_URI = "ui://rigour/dashboard";
|
|
11
|
+
/** Get dashboard HTML with current state injected. */
|
|
12
|
+
export function getDashboardHtml() {
|
|
13
|
+
const stateJson = JSON.stringify(getState()).replace(/'/g, "\\'");
|
|
14
|
+
return DASHBOARD_HTML.replace('__INITIAL_STATE__', stateJson);
|
|
15
|
+
}
|
|
16
|
+
export { getState, pushTimelineEntry, updateScore, updateBrainStatus, setScanning } from './state.js';
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dashboard State — Server-side state accumulator
|
|
3
|
+
*
|
|
4
|
+
* Maintains the current governance state that gets injected into
|
|
5
|
+
* the MCP App dashboard HTML on each resource fetch.
|
|
6
|
+
* Single-threaded Node.js — no locking needed.
|
|
7
|
+
*/
|
|
8
|
+
export interface TimelineEntry {
|
|
9
|
+
timestamp: string;
|
|
10
|
+
tool: string;
|
|
11
|
+
status: string;
|
|
12
|
+
details: string;
|
|
13
|
+
}
|
|
14
|
+
export interface DashboardState {
|
|
15
|
+
currentScore: number | null;
|
|
16
|
+
status: "pass" | "fail" | "scanning" | null;
|
|
17
|
+
timeline: TimelineEntry[];
|
|
18
|
+
severityBreakdown: Record<string, number>;
|
|
19
|
+
brainPatterns: number;
|
|
20
|
+
brainTrend: string | null;
|
|
21
|
+
}
|
|
22
|
+
export declare function getState(): DashboardState;
|
|
23
|
+
export declare function pushTimelineEntry(tool: string, status: string, details: string): void;
|
|
24
|
+
export declare function updateScore(score: number, status: "pass" | "fail", severity?: Record<string, number>): void;
|
|
25
|
+
export declare function updateBrainStatus(patterns: number, trend?: string): void;
|
|
26
|
+
export declare function setScanning(): void;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dashboard State — Server-side state accumulator
|
|
3
|
+
*
|
|
4
|
+
* Maintains the current governance state that gets injected into
|
|
5
|
+
* the MCP App dashboard HTML on each resource fetch.
|
|
6
|
+
* Single-threaded Node.js — no locking needed.
|
|
7
|
+
*/
|
|
8
|
+
const MAX_TIMELINE = 50;
|
|
9
|
+
const state = {
|
|
10
|
+
currentScore: null,
|
|
11
|
+
status: null,
|
|
12
|
+
timeline: [],
|
|
13
|
+
severityBreakdown: {},
|
|
14
|
+
brainPatterns: 0,
|
|
15
|
+
brainTrend: null,
|
|
16
|
+
};
|
|
17
|
+
export function getState() {
|
|
18
|
+
return state;
|
|
19
|
+
}
|
|
20
|
+
export function pushTimelineEntry(tool, status, details) {
|
|
21
|
+
state.timeline.push({
|
|
22
|
+
timestamp: new Date().toISOString(),
|
|
23
|
+
tool,
|
|
24
|
+
status,
|
|
25
|
+
details,
|
|
26
|
+
});
|
|
27
|
+
if (state.timeline.length > MAX_TIMELINE) {
|
|
28
|
+
state.timeline = state.timeline.slice(-MAX_TIMELINE);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
export function updateScore(score, status, severity) {
|
|
32
|
+
state.currentScore = score;
|
|
33
|
+
state.status = status;
|
|
34
|
+
if (severity)
|
|
35
|
+
state.severityBreakdown = severity;
|
|
36
|
+
}
|
|
37
|
+
export function updateBrainStatus(patterns, trend) {
|
|
38
|
+
state.brainPatterns = patterns;
|
|
39
|
+
if (trend)
|
|
40
|
+
state.brainTrend = trend;
|
|
41
|
+
}
|
|
42
|
+
export function setScanning() {
|
|
43
|
+
state.status = "scanning";
|
|
44
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -9,11 +9,14 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
11
11
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
12
|
-
import { CallToolRequestSchema, ListToolsRequestSchema, ListPromptsRequestSchema, GetPromptRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
12
|
+
import { CallToolRequestSchema, ListToolsRequestSchema, ListPromptsRequestSchema, GetPromptRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
13
13
|
import { randomUUID } from "crypto";
|
|
14
14
|
import { GateRunner } from "@rigour-labs/core";
|
|
15
15
|
// Utils
|
|
16
16
|
import { loadConfig, loadMcpSettings, logStudioEvent } from './utils/config.js';
|
|
17
|
+
import { bindServer } from './utils/notifications.js';
|
|
18
|
+
// Dashboard (MCP App)
|
|
19
|
+
import { DASHBOARD_URI, getDashboardHtml, pushTimelineEntry, updateScore } from './dashboard/index.js';
|
|
17
20
|
// Tool definitions
|
|
18
21
|
import { TOOL_DEFINITIONS } from './tools/definitions.js';
|
|
19
22
|
// Tool handlers
|
|
@@ -27,7 +30,9 @@ import { handleHooksCheck, handleHooksInit } from './tools/hooks-handler.js';
|
|
|
27
30
|
import { handleCheckDeep, handleDeepStats } from './tools/deep-handlers.js';
|
|
28
31
|
import { handleMcpGetSettings, handleMcpSetSettings } from './tools/mcp-settings-handler.js';
|
|
29
32
|
// ─── Server Setup ─────────────────────────────────────────────────
|
|
30
|
-
const server = new Server({ name: "rigour-mcp", version: "3.0.1" }, { capabilities: { tools: {}, prompts: {} } });
|
|
33
|
+
const server = new Server({ name: "rigour-mcp", version: "3.0.1" }, { capabilities: { tools: {}, prompts: {}, logging: {}, resources: {} } });
|
|
34
|
+
// Bind server for logging notifications from handlers
|
|
35
|
+
bindServer(server);
|
|
31
36
|
// ─── Tool Listing ─────────────────────────────────────────────────
|
|
32
37
|
// Only expose essential tools by default to improve agent tool selection.
|
|
33
38
|
// Research shows agents degrade at 30+ tools (wrong picks, hallucinated args).
|
|
@@ -38,6 +43,7 @@ const ESSENTIAL_TOOLS = new Set([
|
|
|
38
43
|
'rigour_recall', // Load project memory (START of every task)
|
|
39
44
|
'rigour_remember', // Store conventions/decisions
|
|
40
45
|
'rigour_explain', // Explain gate failures
|
|
46
|
+
'rigour_get_fix_packet', // Get structured fix instructions on FAIL
|
|
41
47
|
'rigour_review', // Review diffs
|
|
42
48
|
'rigour_security_audit', // CVE check
|
|
43
49
|
'rigour_forget', // Remove stored memory
|
|
@@ -45,6 +51,27 @@ const ESSENTIAL_TOOLS = new Set([
|
|
|
45
51
|
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
46
52
|
tools: TOOL_DEFINITIONS.filter(t => ESSENTIAL_TOOLS.has(t.name)),
|
|
47
53
|
}));
|
|
54
|
+
// ─── Resource Listing (MCP App Dashboard) ────────────────────────
|
|
55
|
+
server.setRequestHandler(ListResourcesRequestSchema, async () => ({
|
|
56
|
+
resources: [{
|
|
57
|
+
uri: DASHBOARD_URI,
|
|
58
|
+
name: "Rigour Governance Dashboard",
|
|
59
|
+
description: "Real-time governance dashboard — quality scores, agent activity timeline, severity breakdown",
|
|
60
|
+
mimeType: "text/html",
|
|
61
|
+
}],
|
|
62
|
+
}));
|
|
63
|
+
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
|
|
64
|
+
if (request.params.uri === DASHBOARD_URI) {
|
|
65
|
+
return {
|
|
66
|
+
contents: [{
|
|
67
|
+
uri: DASHBOARD_URI,
|
|
68
|
+
mimeType: "text/html",
|
|
69
|
+
text: getDashboardHtml(),
|
|
70
|
+
}],
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
throw new Error(`Unknown resource: ${request.params.uri}`);
|
|
74
|
+
});
|
|
48
75
|
// ─── Tool Dispatch ────────────────────────────────────────────────
|
|
49
76
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
50
77
|
const { name, arguments: args } = request.params;
|
|
@@ -165,6 +192,29 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
165
192
|
type: "tool_response", requestId, tool: name, status: "success",
|
|
166
193
|
content: result.content, _rigour_report: result._rigour_report,
|
|
167
194
|
});
|
|
195
|
+
// ─── Dashboard State + MCP App UI Hint ──────────────
|
|
196
|
+
const report = result._rigour_report;
|
|
197
|
+
const details = report
|
|
198
|
+
? `${report.status.toUpperCase()} — Score: ${report.stats?.score ?? '?'}/100`
|
|
199
|
+
: "completed";
|
|
200
|
+
pushTimelineEntry(name, result.isError ? "error" : "success", details);
|
|
201
|
+
if (report?.stats) {
|
|
202
|
+
updateScore(report.stats.score ?? 0, report.status === "PASS" ? "pass" : "fail", report.stats.severity_breakdown);
|
|
203
|
+
}
|
|
204
|
+
// Attach UI hint for MCP-App-capable clients (Claude Desktop, VS Code, ChatGPT, Goose)
|
|
205
|
+
if (!result.isError) {
|
|
206
|
+
result._meta = { ui: { resourceUri: DASHBOARD_URI } };
|
|
207
|
+
}
|
|
208
|
+
// Notify clients that the dashboard resource has updated
|
|
209
|
+
try {
|
|
210
|
+
await server.notification({
|
|
211
|
+
method: "notifications/resources/updated",
|
|
212
|
+
params: { uri: DASHBOARD_URI },
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
catch {
|
|
216
|
+
// Client doesn't support resource notifications — fine
|
|
217
|
+
}
|
|
168
218
|
return result;
|
|
169
219
|
}
|
|
170
220
|
catch (error) {
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* @since v4.0.0
|
|
7
7
|
*/
|
|
8
8
|
import path from "path";
|
|
9
|
+
import { notifyProgress } from '../utils/notifications.js';
|
|
9
10
|
function resolveDeepExecution(args) {
|
|
10
11
|
const requestedProvider = (args.provider || '').toLowerCase();
|
|
11
12
|
const isForcedLocal = requestedProvider === 'local';
|
|
@@ -31,7 +32,10 @@ export async function handleCheckDeep(runner, cwd, config, args) {
|
|
|
31
32
|
apiBaseUrl: args.apiBaseUrl,
|
|
32
33
|
modelName: args.modelName,
|
|
33
34
|
};
|
|
35
|
+
notifyProgress("info", `Deep analysis starting (${execution.isLocal ? 'local sidecar' : execution.provider})...`);
|
|
34
36
|
const report = await runner.run(cwd, undefined, deepOpts);
|
|
37
|
+
const notifyScore = report.stats.score ?? 100;
|
|
38
|
+
notifyProgress(report.status === "PASS" ? "info" : "warning", `Deep analysis complete: ${notifyScore}/100 \u2014 ${report.failures.length} findings`);
|
|
35
39
|
// Persist to SQLite (best-effort). Skip during tests to avoid CI timing flakes.
|
|
36
40
|
if (!isTestRuntime()) {
|
|
37
41
|
try {
|
|
@@ -27,7 +27,7 @@ export const TOOL_DEFINITIONS = [
|
|
|
27
27
|
// ─── Core Quality Gates ───────────────────────────────
|
|
28
28
|
{
|
|
29
29
|
name: "rigour_check",
|
|
30
|
-
description: "Run quality gate checks on the project. MUST be called before declaring any coding task complete. Checks code complexity, file size, required docs, security patterns, and more. Returns PASS or FAIL with details.",
|
|
30
|
+
description: "Run quality gate checks on the project. MUST be called before declaring any coding task complete. Checks code complexity, file size, required docs, security patterns, and more. Returns PASS or FAIL with details. **Always show the user the headline summary from this tool's output** — it tells them what Rigour caught. If FAIL, call rigour_get_fix_packet for structured fix instructions with exact file locations and step-by-step remediation.",
|
|
31
31
|
inputSchema: {
|
|
32
32
|
type: "object",
|
|
33
33
|
properties: {
|
|
@@ -52,7 +52,7 @@ export const TOOL_DEFINITIONS = [
|
|
|
52
52
|
},
|
|
53
53
|
{
|
|
54
54
|
name: "rigour_explain",
|
|
55
|
-
description: "Explain
|
|
55
|
+
description: "Explain WHY quality gates failed in human-readable language. Use this to understand the reasoning behind each violation before fixing. For machine-readable fix instructions, use rigour_get_fix_packet instead.",
|
|
56
56
|
inputSchema: {
|
|
57
57
|
type: "object",
|
|
58
58
|
properties: cwdParam(),
|
|
@@ -84,7 +84,7 @@ export const TOOL_DEFINITIONS = [
|
|
|
84
84
|
},
|
|
85
85
|
{
|
|
86
86
|
name: "rigour_get_fix_packet",
|
|
87
|
-
description: "
|
|
87
|
+
description: "Call this after rigour_check returns FAIL. Returns a prioritized Fix Packet with exact violations, file locations, and step-by-step fix instructions. Apply fixes in severity order (critical first), then re-run rigour_check to verify. Repeat until PASS. **After applying all fixes, tell the user what Rigour caught and that fixes were applied.**",
|
|
88
88
|
inputSchema: {
|
|
89
89
|
type: "object",
|
|
90
90
|
properties: cwdParam(),
|
|
@@ -9,6 +9,7 @@ import fs from "fs-extra";
|
|
|
9
9
|
import path from "path";
|
|
10
10
|
import { FixPacketService } from "@rigour-labs/core";
|
|
11
11
|
import { logStudioEvent } from '../utils/config.js';
|
|
12
|
+
import { notifyProgress } from '../utils/notifications.js';
|
|
12
13
|
export async function handleRun(cwd, command, requestId) {
|
|
13
14
|
// 1. Log Interceptable Event
|
|
14
15
|
await logStudioEvent(cwd, {
|
|
@@ -54,8 +55,10 @@ export async function handleRunSupervised(runner, cwd, command, maxRetries, dryR
|
|
|
54
55
|
maxRetries,
|
|
55
56
|
dryRun,
|
|
56
57
|
});
|
|
58
|
+
notifyProgress("info", `Supervisor started: ${command} (max ${maxRetries} retries)`);
|
|
57
59
|
while (iteration < maxRetries) {
|
|
58
60
|
iteration++;
|
|
61
|
+
notifyProgress("info", `Supervisor iteration ${iteration}/${maxRetries}...`);
|
|
59
62
|
if (!dryRun) {
|
|
60
63
|
try {
|
|
61
64
|
await execa(command, { shell: true, cwd });
|
|
@@ -84,6 +87,7 @@ export async function handleRunSupervised(runner, cwd, command, maxRetries, dryR
|
|
|
84
87
|
failedGates: failedGateIds,
|
|
85
88
|
});
|
|
86
89
|
if (lastReport.status === "PASS") {
|
|
90
|
+
notifyProgress("info", `Supervisor PASSED on iteration ${iteration} \u2014 Score: ${lastReport.stats.score ?? '?'}/100`);
|
|
87
91
|
const score = lastReport.stats.score !== undefined ? ` | Score: ${lastReport.stats.score}/100` : '';
|
|
88
92
|
result = {
|
|
89
93
|
content: [{
|
|
@@ -94,6 +98,7 @@ export async function handleRunSupervised(runner, cwd, command, maxRetries, dryR
|
|
|
94
98
|
break;
|
|
95
99
|
}
|
|
96
100
|
if (iteration >= maxRetries) {
|
|
101
|
+
notifyProgress("error", `Supervisor FAILED after ${iteration} iterations \u2014 ${lastReport.failures.length} violations remain`);
|
|
97
102
|
// Use FixPacketService for structured output instead of ad-hoc formatting
|
|
98
103
|
let fixPacketText;
|
|
99
104
|
if (config) {
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* @since v2.17.0 — extracted from monolithic index.ts
|
|
7
7
|
*/
|
|
8
8
|
import { PatternMatcher, loadPatternIndex, getDefaultIndexPath, StalenessDetector, SecurityDetector, } from "@rigour-labs/core/pattern-index";
|
|
9
|
+
import { notifyProgress } from '../utils/notifications.js';
|
|
9
10
|
export async function handleCheckPattern(cwd, patternName, type, intent) {
|
|
10
11
|
const indexPath = getDefaultIndexPath(cwd);
|
|
11
12
|
const index = await loadPatternIndex(indexPath);
|
|
@@ -66,7 +67,9 @@ export async function handleCheckPattern(cwd, patternName, type, intent) {
|
|
|
66
67
|
return { content: [{ type: "text", text: resultText }] };
|
|
67
68
|
}
|
|
68
69
|
export async function handleSecurityAudit(cwd) {
|
|
70
|
+
notifyProgress("info", "Running CVE security audit...");
|
|
69
71
|
const security = new SecurityDetector(cwd);
|
|
70
72
|
const summary = await security.getSecuritySummary();
|
|
73
|
+
notifyProgress("info", "Security audit complete");
|
|
71
74
|
return { content: [{ type: "text", text: summary }] };
|
|
72
75
|
}
|
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Quality Gate Tool Handlers
|
|
3
|
+
*
|
|
4
|
+
* Handlers for: rigour_check, rigour_explain, rigour_status,
|
|
5
|
+
* rigour_get_fix_packet, rigour_list_gates, rigour_get_config
|
|
6
|
+
*
|
|
7
|
+
* @since v2.17.0 — extracted from monolithic index.ts
|
|
8
|
+
*/
|
|
9
|
+
import { renderMcpHeadline, renderFixAttribution } from "@rigour-labs/core";
|
|
10
|
+
import { notifyProgress } from '../utils/notifications.js';
|
|
1
11
|
function resolveDeepExecution(args) {
|
|
2
12
|
const requestedProvider = (args.provider || '').toLowerCase();
|
|
3
13
|
const isForcedLocal = requestedProvider === 'local';
|
|
@@ -42,7 +52,22 @@ export async function handleCheck(runner, cwd, args = {}) {
|
|
|
42
52
|
modelName: args.modelName,
|
|
43
53
|
};
|
|
44
54
|
}
|
|
55
|
+
notifyProgress("info", fileTargets ? `Scanning ${fileTargets.length} files...` : "Scanning project...");
|
|
56
|
+
if (deepMode !== 'off') {
|
|
57
|
+
notifyProgress("info", `Deep analysis: ${execution.isLocal ? 'local sidecar' : execution.provider} (${deepMode} mode)`);
|
|
58
|
+
}
|
|
45
59
|
const report = await runner.run(cwd, fileTargets, deepOpts);
|
|
60
|
+
if (report.status === "PASS") {
|
|
61
|
+
notifyProgress("info", `PASS \u2014 Score: ${report.stats.score ?? '?'}/100`);
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
const sev = report.stats.severity_breakdown || {};
|
|
65
|
+
const sevParts = Object.entries(sev).filter(([, c]) => c > 0).map(([s, c]) => `${c} ${s}`).join(', ');
|
|
66
|
+
notifyProgress("warning", `FAIL \u2014 ${sevParts || report.failures.length + ' violations'} (Score: ${report.stats.score ?? '?'}/100)`);
|
|
67
|
+
const worst = report.failures.find(f => f.severity === 'critical') || report.failures[0];
|
|
68
|
+
if (worst)
|
|
69
|
+
notifyProgress("warning", `${worst.title}${worst.files?.[0] ? ' in ' + worst.files[0] : ''}`);
|
|
70
|
+
}
|
|
46
71
|
const scoreText = formatScoreText(report.stats);
|
|
47
72
|
const sevText = formatSeverityText(report.stats);
|
|
48
73
|
const deepText = deepMode === 'off'
|
|
@@ -51,10 +76,12 @@ export async function handleCheck(runner, cwd, args = {}) {
|
|
|
51
76
|
`${execution.isLocal
|
|
52
77
|
? '\nPrivacy: Local sidecar/model execution. Code remains on this machine.'
|
|
53
78
|
: `\nPrivacy: Cloud provider execution. Code context may be sent to ${execution.provider} API.`}`;
|
|
79
|
+
// Human-facing headline — agents naturally pass this through to users
|
|
80
|
+
const headline = renderMcpHeadline(report);
|
|
54
81
|
const result = {
|
|
55
82
|
content: [{
|
|
56
83
|
type: "text",
|
|
57
|
-
text:
|
|
84
|
+
text: `${headline}\n\n${scoreText.trim()}${sevText}${deepText}\n\nSummary:\n${Object.entries(report.summary).map(([k, v]) => `- ${k}: ${v}`).join("\n")}`,
|
|
58
85
|
}],
|
|
59
86
|
};
|
|
60
87
|
result._rigour_report = report;
|
|
@@ -105,11 +132,18 @@ export async function handleGetFixPacket(runner, cwd, config) {
|
|
|
105
132
|
content: [{ type: "text", text: `ALL QUALITY GATES PASSED.${passScore} The current state meets the required engineering standards.` }],
|
|
106
133
|
};
|
|
107
134
|
}
|
|
135
|
+
notifyProgress("info", `Generating fix packet for ${report.failures.length} violations...`);
|
|
108
136
|
const { FixPacketService } = await import("@rigour-labs/core");
|
|
109
137
|
const fixPacketService = new FixPacketService();
|
|
110
138
|
const fixPacket = fixPacketService.generate(report, config);
|
|
139
|
+
// Find worst violation for attribution
|
|
140
|
+
const worst = report.failures.find(f => f.severity === 'critical')
|
|
141
|
+
|| report.failures.find(f => f.severity === 'high')
|
|
142
|
+
|| report.failures[0];
|
|
143
|
+
const worstLabel = worst ? worst.title : 'quality violations';
|
|
144
|
+
const attribution = renderFixAttribution(report.failures.length, worstLabel);
|
|
111
145
|
return {
|
|
112
|
-
content: [{ type: "text", text: formatFixPacketText(fixPacket, report) }],
|
|
146
|
+
content: [{ type: "text", text: formatFixPacketText(fixPacket, report) + attribution }],
|
|
113
147
|
};
|
|
114
148
|
}
|
|
115
149
|
/**
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { parseDiff } from '../utils/config.js';
|
|
2
|
+
import { notifyProgress } from '../utils/notifications.js';
|
|
2
3
|
export async function handleReview(runner, cwd, diff, changedFiles) {
|
|
3
4
|
// 1. Map diff to line numbers for filtering
|
|
4
5
|
const diffMapping = parseDiff(diff);
|
|
5
6
|
const targetFiles = changedFiles || Object.keys(diffMapping);
|
|
6
7
|
// 2. Run high-fidelity analysis on changed files
|
|
8
|
+
notifyProgress("info", `Reviewing ${targetFiles.length} changed files...`);
|
|
7
9
|
const report = await runner.run(cwd, targetFiles);
|
|
8
10
|
// 3. Filter failures to only those on changed lines (or global gate failures)
|
|
9
11
|
const filteredFailures = report.failures.filter(failure => {
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Logging Notifications — Singleton
|
|
3
|
+
*
|
|
4
|
+
* Gives all handler modules access to server.sendLoggingMessage()
|
|
5
|
+
* without threading the server instance through every function signature.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* bindServer(server) — call once at startup
|
|
9
|
+
* notifyProgress("info", msg) — fire-and-forget from any handler
|
|
10
|
+
*/
|
|
11
|
+
import type { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
12
|
+
/** Bind the MCP server instance (call once at startup). */
|
|
13
|
+
export declare function bindServer(server: Server): void;
|
|
14
|
+
/**
|
|
15
|
+
* Send a logging notification to the MCP client.
|
|
16
|
+
* Fire-and-forget — silently drops if client doesn't support logging.
|
|
17
|
+
*/
|
|
18
|
+
export declare function notifyProgress(level: "debug" | "info" | "notice" | "warning" | "error" | "critical", message: string, logger?: string): void;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
let _server = null;
|
|
2
|
+
/** Bind the MCP server instance (call once at startup). */
|
|
3
|
+
export function bindServer(server) {
|
|
4
|
+
_server = server;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Send a logging notification to the MCP client.
|
|
8
|
+
* Fire-and-forget — silently drops if client doesn't support logging.
|
|
9
|
+
*/
|
|
10
|
+
export function notifyProgress(level, message, logger = "rigour") {
|
|
11
|
+
if (!_server)
|
|
12
|
+
return;
|
|
13
|
+
try {
|
|
14
|
+
void _server.sendLoggingMessage({ level, logger, data: message });
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
// Client may not support logging — graceful fallback
|
|
18
|
+
}
|
|
19
|
+
}
|
package/package.json
CHANGED
|
@@ -1,30 +1,44 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rigour-labs/mcp",
|
|
3
|
-
"version": "5.2.
|
|
4
|
-
"description": "MCP server for AI code governance — OWASP LLM Top 10 (10/10), real-time
|
|
3
|
+
"version": "5.2.5",
|
|
4
|
+
"description": "MCP server + live dashboard for AI code governance — OWASP LLM Top 10 (10/10), real-time MCP App UI, 25+ security patterns, Bayesian learning Brain, hallucinated import detection, multi-agent governance. Works with Claude, Cursor, VS Code, ChatGPT, Goose, Windsurf. Industry presets for HIPAA, SOC2, FedRAMP.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://rigour.run",
|
|
7
7
|
"icon": "https://raw.githubusercontent.com/rigour-labs/rigour/main/docs/assets/icon.svg",
|
|
8
8
|
"keywords": [
|
|
9
|
+
"ai",
|
|
10
|
+
"llm",
|
|
9
11
|
"mcp",
|
|
12
|
+
"mcp-app",
|
|
13
|
+
"mcp-server",
|
|
10
14
|
"model-context-protocol",
|
|
11
|
-
"
|
|
15
|
+
"ai-agent",
|
|
12
16
|
"ai-code-governance",
|
|
17
|
+
"owasp-llm-top-10",
|
|
18
|
+
"bayesian-learning",
|
|
13
19
|
"real-time-hooks",
|
|
14
20
|
"security-patterns",
|
|
15
21
|
"hallucinated-imports",
|
|
16
|
-
"
|
|
17
|
-
"hipaa",
|
|
18
|
-
"soc2",
|
|
19
|
-
"fedramp",
|
|
22
|
+
"vibe-coding",
|
|
20
23
|
"claude",
|
|
21
24
|
"cursor",
|
|
22
25
|
"cline",
|
|
23
26
|
"windsurf",
|
|
27
|
+
"copilot",
|
|
28
|
+
"gemini",
|
|
29
|
+
"codex",
|
|
30
|
+
"chatgpt",
|
|
31
|
+
"goose",
|
|
24
32
|
"multi-agent-governance",
|
|
25
33
|
"quality-gates",
|
|
26
|
-
"
|
|
27
|
-
"
|
|
34
|
+
"fix-packets",
|
|
35
|
+
"agent-governance",
|
|
36
|
+
"drift-detection",
|
|
37
|
+
"live-dashboard",
|
|
38
|
+
"code-quality",
|
|
39
|
+
"ai-safety",
|
|
40
|
+
"deep-learning",
|
|
41
|
+
"rlaif"
|
|
28
42
|
],
|
|
29
43
|
"type": "module",
|
|
30
44
|
"mcpName": "io.github.rigour-labs/rigour",
|
|
@@ -48,7 +62,7 @@
|
|
|
48
62
|
"execa": "^8.0.1",
|
|
49
63
|
"fs-extra": "^11.2.0",
|
|
50
64
|
"yaml": "^2.8.2",
|
|
51
|
-
"@rigour-labs/core": "5.2.
|
|
65
|
+
"@rigour-labs/core": "5.2.5"
|
|
52
66
|
},
|
|
53
67
|
"devDependencies": {
|
|
54
68
|
"@types/node": "^25.0.3",
|