@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 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.3.2') }, { capabilities: { tools: {}, prompts: {}, logging: {}, resources: {} } });
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 cachedText = JSON.stringify(cached, null, 2);
132
- const telemetry = scopeTelemetryMeta({
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
- const text = `CONTEXT SCOPE (cached)\n\n${cachedText}`;
141
- return {
142
- content: [{ type: 'text', text: appendContextFooter(text, telemetry, 'rigour_check_pattern before creating code') }],
143
- _telemetry: telemetry,
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.3)
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.4.0",
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.4.0"
65
+ "@rigour-labs/core": "5.5.0"
66
66
  },
67
67
  "devDependencies": {
68
68
  "@types/node": "^25.0.3",