@rigour-labs/mcp 5.4.0 → 5.5.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 +1 -1
- package/dist/tools/context-handlers.js +78 -12
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -34,7 +34,7 @@ import { handleContextStats, handleTaskCost, handleCacheStats, handleContextExpl
|
|
|
34
34
|
import { handleIndex } from './tools/index-handlers.js';
|
|
35
35
|
import { recordContextEvent, estimateTokenCount } from '@rigour-labs/core';
|
|
36
36
|
// ─── Server Setup ─────────────────────────────────────────────────
|
|
37
|
-
const server = new Server({ name: "rigour-mcp", version: getMcpVersion('5.
|
|
37
|
+
const server = new Server({ name: "rigour-mcp", version: getMcpVersion('5.5.0') }, { capabilities: { tools: {}, prompts: {}, logging: {}, resources: {} } });
|
|
38
38
|
// Bind server for logging notifications from handlers
|
|
39
39
|
bindServer(server);
|
|
40
40
|
// ─── Tool Listing ─────────────────────────────────────────────────
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
import fs from 'fs-extra';
|
|
12
12
|
import path from 'path';
|
|
13
13
|
import { createHash } from 'crypto';
|
|
14
|
-
import { getTaskContextStats, getTaskCostStats, getCacheStats, explainContext, getSemanticQueryCache, setSemanticQueryCache, estimateTokenCount, } from '@rigour-labs/core';
|
|
14
|
+
import { getTaskContextStats, getTaskCostStats, getCacheStats, explainContext, getSemanticQueryCache, setSemanticQueryCache, findRelatedSemanticQueryCache, filterExistingEditScope, estimateTokenCount, } from '@rigour-labs/core';
|
|
15
15
|
import { loadPatternIndex, getDefaultIndexPath, generateEmbedding, semanticSearch, } from '@rigour-labs/core/pattern-index';
|
|
16
16
|
import { buildTelemetryMeta, getWorkspaceCommitSha } from '../utils/context-telemetry.js';
|
|
17
17
|
import { appendContextFooter } from '../utils/context-footer.js';
|
|
@@ -122,26 +122,88 @@ function textMatchPatterns(query, patterns, limit) {
|
|
|
122
122
|
.slice(0, limit)
|
|
123
123
|
.map(s => s.pattern);
|
|
124
124
|
}
|
|
125
|
+
async function serveCachedScope(cached, opts) {
|
|
126
|
+
const originalScope = cached.editScope || [];
|
|
127
|
+
const { valid, missing } = await filterExistingEditScope(originalScope, opts.cwd);
|
|
128
|
+
// Quality guard: require majority of scoped files still present, else recompute.
|
|
129
|
+
if (originalScope.length > 0) {
|
|
130
|
+
const retention = valid.length / originalScope.length;
|
|
131
|
+
if (valid.length === 0 || retention < 0.5) {
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
const validSet = new Set(valid);
|
|
136
|
+
const validationScope = (cached.validationScope || []).filter((item) => {
|
|
137
|
+
// Keep entries that don't look like file paths, or whose path still exists.
|
|
138
|
+
const maybePath = item.replace(/^review\s+/i, '').trim();
|
|
139
|
+
if (!maybePath.includes('/') && !maybePath.endsWith('.ts') && !maybePath.endsWith('.tsx')) {
|
|
140
|
+
return true;
|
|
141
|
+
}
|
|
142
|
+
return validSet.has(maybePath) || valid.some((f) => item.includes(f));
|
|
143
|
+
});
|
|
144
|
+
const evidence = (cached.evidence || []).filter((line) => {
|
|
145
|
+
if (missing.length === 0)
|
|
146
|
+
return true;
|
|
147
|
+
return !missing.some((m) => line.includes(m));
|
|
148
|
+
});
|
|
149
|
+
const payload = {
|
|
150
|
+
...cached,
|
|
151
|
+
editScope: valid,
|
|
152
|
+
validationScope,
|
|
153
|
+
evidence,
|
|
154
|
+
staleFilesDropped: missing,
|
|
155
|
+
cacheNote: opts.note,
|
|
156
|
+
};
|
|
157
|
+
const cachedText = JSON.stringify(payload, null, 2);
|
|
158
|
+
const telemetry = scopeTelemetryMeta({
|
|
159
|
+
candidateText: opts.fullIndexCandidate,
|
|
160
|
+
returnedText: cachedText,
|
|
161
|
+
cacheStatus: opts.cacheStatus,
|
|
162
|
+
query: opts.query,
|
|
163
|
+
taskId: opts.taskId,
|
|
164
|
+
agentId: opts.agentId,
|
|
165
|
+
});
|
|
166
|
+
const label = opts.cacheStatus === 'partial-hit'
|
|
167
|
+
? 'CONTEXT SCOPE (partial cache)'
|
|
168
|
+
: 'CONTEXT SCOPE (cached)';
|
|
169
|
+
const text = `${label}\n\n${cachedText}`;
|
|
170
|
+
return {
|
|
171
|
+
content: [{ type: 'text', text: appendContextFooter(text, telemetry, 'rigour_check_pattern before creating code') }],
|
|
172
|
+
_telemetry: telemetry,
|
|
173
|
+
};
|
|
174
|
+
}
|
|
125
175
|
export async function handleContextScope(cwd, query, limit = 10, taskId, agentId) {
|
|
126
176
|
const commitSha = await getWorkspaceCommitSha(cwd);
|
|
127
177
|
const indexPath = getDefaultIndexPath(cwd);
|
|
128
178
|
const fullIndexCandidate = `Full repository scan for: ${query}`;
|
|
129
179
|
const cached = await getSemanticQueryCache(query, commitSha, cwd);
|
|
130
180
|
if (cached) {
|
|
131
|
-
const
|
|
132
|
-
|
|
133
|
-
candidateText: fullIndexCandidate,
|
|
134
|
-
returnedText: cachedText,
|
|
135
|
-
cacheStatus: 'semantic-hit',
|
|
181
|
+
const served = await serveCachedScope(cached, {
|
|
182
|
+
cwd,
|
|
136
183
|
query,
|
|
184
|
+
fullIndexCandidate,
|
|
185
|
+
// Exact query+commit key hit — content-addressed semantic layer.
|
|
186
|
+
cacheStatus: 'exact-hit',
|
|
137
187
|
taskId,
|
|
138
188
|
agentId,
|
|
139
189
|
});
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
190
|
+
if (served)
|
|
191
|
+
return served;
|
|
192
|
+
}
|
|
193
|
+
// Quality-safe power-up: reuse highly overlapping queries at the same commit only.
|
|
194
|
+
const related = await findRelatedSemanticQueryCache(query, commitSha, cwd);
|
|
195
|
+
if (related) {
|
|
196
|
+
const served = await serveCachedScope(related.entry, {
|
|
197
|
+
cwd,
|
|
198
|
+
query,
|
|
199
|
+
fullIndexCandidate,
|
|
200
|
+
cacheStatus: 'partial-hit',
|
|
201
|
+
taskId,
|
|
202
|
+
agentId,
|
|
203
|
+
note: `Reused related query at same commit (overlap ${(related.overlap * 100).toFixed(0)}%): "${related.sourceQuery}"`,
|
|
204
|
+
});
|
|
205
|
+
if (served)
|
|
206
|
+
return served;
|
|
145
207
|
}
|
|
146
208
|
const index = await loadPatternIndex(indexPath);
|
|
147
209
|
if (!index) {
|
|
@@ -165,12 +227,16 @@ export async function handleContextScope(cwd, query, limit = 10, taskId, agentId
|
|
|
165
227
|
try {
|
|
166
228
|
const queryVector = await generateEmbedding(query);
|
|
167
229
|
const similarities = semanticSearch(queryVector, index.patterns);
|
|
230
|
+
// Slightly stricter threshold — quality over vanity hit-rate; partial cache covers near-queries.
|
|
168
231
|
matched = index.patterns
|
|
169
232
|
.map((p, i) => ({ pattern: p, similarity: similarities[i] ?? 0 }))
|
|
170
|
-
.filter(r => r.similarity > 0.
|
|
233
|
+
.filter(r => r.similarity > 0.4)
|
|
171
234
|
.sort((a, b) => b.similarity - a.similarity)
|
|
172
235
|
.slice(0, limit)
|
|
173
236
|
.map(r => r.pattern);
|
|
237
|
+
if (matched.length === 0) {
|
|
238
|
+
matched = textMatchPatterns(query, index.patterns, limit);
|
|
239
|
+
}
|
|
174
240
|
}
|
|
175
241
|
catch {
|
|
176
242
|
matched = textMatchPatterns(query, index.patterns, limit);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rigour-labs/mcp",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.5.0",
|
|
4
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",
|
|
@@ -62,7 +62,7 @@
|
|
|
62
62
|
"execa": "^8.0.1",
|
|
63
63
|
"fs-extra": "^11.2.0",
|
|
64
64
|
"yaml": "^2.8.2",
|
|
65
|
-
"@rigour-labs/core": "5.
|
|
65
|
+
"@rigour-labs/core": "5.5.0"
|
|
66
66
|
},
|
|
67
67
|
"devDependencies": {
|
|
68
68
|
"@types/node": "^25.0.3",
|