@crowley/rag-mcp 1.0.4 → 1.0.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/context-enrichment.d.ts +2 -2
- package/dist/context-enrichment.js +30 -14
- package/dist/index.js +18 -0
- package/dist/tools/architecture.js +8 -0
- package/dist/tools/indexing.js +13 -1
- package/dist/tools/suggestions.js +276 -0
- package/package.json +1 -1
|
@@ -26,9 +26,9 @@ export declare class ContextEnricher {
|
|
|
26
26
|
*/
|
|
27
27
|
before(name: string, args: Record<string, unknown>, ctx: ToolContext): Promise<string | null>;
|
|
28
28
|
/**
|
|
29
|
-
* After hook: fire-and-forget session activity tracking.
|
|
29
|
+
* After hook: fire-and-forget session activity tracking + implicit feedback.
|
|
30
30
|
*/
|
|
31
|
-
after(name: string,
|
|
31
|
+
after(name: string, args: Record<string, unknown>, result: string, ctx: ToolContext): void;
|
|
32
32
|
/**
|
|
33
33
|
* Extract a semantic query string from tool arguments.
|
|
34
34
|
*/
|
|
@@ -18,6 +18,7 @@ export const DEFAULT_ENRICHABLE_TOOLS = new Set([
|
|
|
18
18
|
"suggest_implementation",
|
|
19
19
|
"suggest_related_code",
|
|
20
20
|
"check_architecture",
|
|
21
|
+
"context_briefing",
|
|
21
22
|
"run_agent",
|
|
22
23
|
]);
|
|
23
24
|
export const DEFAULT_SKIP_TOOLS = new Set([
|
|
@@ -79,21 +80,36 @@ export class ContextEnricher {
|
|
|
79
80
|
}
|
|
80
81
|
}
|
|
81
82
|
/**
|
|
82
|
-
* After hook: fire-and-forget session activity tracking.
|
|
83
|
+
* After hook: fire-and-forget session activity tracking + implicit feedback.
|
|
83
84
|
*/
|
|
84
|
-
after(name,
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
85
|
+
after(name, args, result, ctx) {
|
|
86
|
+
// Session activity tracking
|
|
87
|
+
if (ctx.activeSessionId) {
|
|
88
|
+
ctx.api
|
|
89
|
+
.post(`/api/session/${ctx.activeSessionId}/activity`, {
|
|
90
|
+
projectName: ctx.projectName,
|
|
91
|
+
type: "tool",
|
|
92
|
+
value: name,
|
|
93
|
+
})
|
|
94
|
+
.catch(() => { });
|
|
95
|
+
}
|
|
96
|
+
// Implicit positive feedback for enrichable search tools
|
|
97
|
+
if (this.config.enrichableTools.has(name)) {
|
|
98
|
+
const query = this.extractQuery(args);
|
|
99
|
+
if (query &&
|
|
100
|
+
result &&
|
|
101
|
+
!result.includes("No results") &&
|
|
102
|
+
!result.includes("not found") &&
|
|
103
|
+
!result.includes("No relevant context found")) {
|
|
104
|
+
ctx.api
|
|
105
|
+
.post("/api/feedback/search", {
|
|
106
|
+
projectName: ctx.projectName,
|
|
107
|
+
query,
|
|
108
|
+
feedbackType: "helpful",
|
|
109
|
+
})
|
|
110
|
+
.catch(() => { });
|
|
111
|
+
}
|
|
112
|
+
}
|
|
97
113
|
}
|
|
98
114
|
/**
|
|
99
115
|
* Extract a semantic query string from tool arguments.
|
package/dist/index.js
CHANGED
|
@@ -99,6 +99,24 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
99
99
|
content: [{ type: "text", text: result }],
|
|
100
100
|
};
|
|
101
101
|
});
|
|
102
|
+
// Graceful shutdown: close active session on exit
|
|
103
|
+
async function cleanup() {
|
|
104
|
+
if (ctx.activeSessionId) {
|
|
105
|
+
try {
|
|
106
|
+
await api.post(`/api/session/${ctx.activeSessionId}/end`, {
|
|
107
|
+
projectName: PROJECT_NAME,
|
|
108
|
+
summary: "Session ended by MCP server shutdown",
|
|
109
|
+
autoSaveLearnings: true,
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
catch {
|
|
113
|
+
// Best-effort, don't block shutdown
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
process.exit(0);
|
|
117
|
+
}
|
|
118
|
+
process.on("SIGINT", cleanup);
|
|
119
|
+
process.on("SIGTERM", cleanup);
|
|
102
120
|
// Start server
|
|
103
121
|
async function main() {
|
|
104
122
|
const transport = new StdioServerTransport();
|
|
@@ -291,6 +291,7 @@ ${alternatives ? `## Alternatives Considered\n${alternatives}` : ""}`;
|
|
|
291
291
|
query: query || "architecture decision ADR",
|
|
292
292
|
type: "decision",
|
|
293
293
|
limit,
|
|
294
|
+
tag: "adr",
|
|
294
295
|
});
|
|
295
296
|
const results = response.data.results || [];
|
|
296
297
|
const adrs = results.filter((r) => r.memory.tags?.includes("adr") &&
|
|
@@ -349,6 +350,7 @@ ${appliesTo ? `## Applies To\n${appliesTo}` : ""}`;
|
|
|
349
350
|
query: query || "architectural pattern structure",
|
|
350
351
|
type: "context",
|
|
351
352
|
limit,
|
|
353
|
+
tag: "pattern",
|
|
352
354
|
});
|
|
353
355
|
const results = response.data.results || [];
|
|
354
356
|
const patterns = results.filter((r) => {
|
|
@@ -383,6 +385,7 @@ ${appliesTo ? `## Applies To\n${appliesTo}` : ""}`;
|
|
|
383
385
|
query: patternQuery,
|
|
384
386
|
type: "context",
|
|
385
387
|
limit: 5,
|
|
388
|
+
tag: "pattern",
|
|
386
389
|
});
|
|
387
390
|
// Get relevant ADRs
|
|
388
391
|
const adrsResponse = await ctx.api.post("/api/memory/recall", {
|
|
@@ -390,6 +393,7 @@ ${appliesTo ? `## Applies To\n${appliesTo}` : ""}`;
|
|
|
390
393
|
query: patternQuery,
|
|
391
394
|
type: "decision",
|
|
392
395
|
limit: 5,
|
|
396
|
+
tag: "adr",
|
|
393
397
|
});
|
|
394
398
|
// Search similar code in codebase
|
|
395
399
|
let similarCode = [];
|
|
@@ -499,6 +503,7 @@ Provide a structured analysis:
|
|
|
499
503
|
query: `${type} ${feature} pattern structure`,
|
|
500
504
|
type: "context",
|
|
501
505
|
limit: 5,
|
|
506
|
+
tag: "pattern",
|
|
502
507
|
});
|
|
503
508
|
// Get relevant ADRs
|
|
504
509
|
const adrsResponse = await ctx.api.post("/api/memory/recall", {
|
|
@@ -506,6 +511,7 @@ Provide a structured analysis:
|
|
|
506
511
|
query: `${type} ${feature}`,
|
|
507
512
|
type: "decision",
|
|
508
513
|
limit: 3,
|
|
514
|
+
tag: "adr",
|
|
509
515
|
});
|
|
510
516
|
// Get similar implementations
|
|
511
517
|
const codeResponse = await ctx.api.post("/api/search", {
|
|
@@ -602,6 +608,7 @@ ${relatedAdr ? `## Related ADR\n${relatedAdr}` : ""}`;
|
|
|
602
608
|
query: "technical debt violation issue",
|
|
603
609
|
type: "insight",
|
|
604
610
|
limit: limit * 2, // Fetch extra to account for filtering
|
|
611
|
+
tag: "tech-debt",
|
|
605
612
|
});
|
|
606
613
|
const results = response.data.results || [];
|
|
607
614
|
const debts = results
|
|
@@ -649,6 +656,7 @@ ${relatedAdr ? `## Related ADR\n${relatedAdr}` : ""}`;
|
|
|
649
656
|
query: "pattern structure organization",
|
|
650
657
|
type: "context",
|
|
651
658
|
limit: 10,
|
|
659
|
+
tag: "pattern",
|
|
652
660
|
});
|
|
653
661
|
// Get codebase structure
|
|
654
662
|
const codeResponse = await ctx.api.post("/api/search", {
|
package/dist/tools/indexing.js
CHANGED
|
@@ -100,6 +100,9 @@ async function uploadFiles(ctx, projectPath, opts) {
|
|
|
100
100
|
duration: totalDuration,
|
|
101
101
|
};
|
|
102
102
|
}
|
|
103
|
+
// In-memory cache for get_index_status (30 min TTL)
|
|
104
|
+
let _statusCache = null;
|
|
105
|
+
const STATUS_CACHE_TTL = 30 * 60 * 1000; // 30 minutes
|
|
103
106
|
/**
|
|
104
107
|
* Create the indexing tools module with project-specific descriptions.
|
|
105
108
|
*/
|
|
@@ -125,7 +128,7 @@ export function createIndexingTools(projectName) {
|
|
|
125
128
|
},
|
|
126
129
|
{
|
|
127
130
|
name: "get_index_status",
|
|
128
|
-
description: `Get the indexing status for ${projectName} codebase.`,
|
|
131
|
+
description: `Get the indexing status for ${projectName} codebase. Results cached for 30 minutes.`,
|
|
129
132
|
inputSchema: {
|
|
130
133
|
type: "object",
|
|
131
134
|
properties: {},
|
|
@@ -168,6 +171,7 @@ export function createIndexingTools(projectName) {
|
|
|
168
171
|
const { path: indexPath, force = false } = args;
|
|
169
172
|
const projectPath = indexPath || ctx.projectPath;
|
|
170
173
|
const stats = await uploadFiles(ctx, projectPath, { force });
|
|
174
|
+
_statusCache = null; // Invalidate status cache after indexing
|
|
171
175
|
let result = `## Indexing ${projectName}\n\n`;
|
|
172
176
|
result += `- **Total files found:** ${stats.totalFiles}\n`;
|
|
173
177
|
result += `- **Files indexed:** ${stats.indexedFiles}\n`;
|
|
@@ -177,6 +181,11 @@ export function createIndexingTools(projectName) {
|
|
|
177
181
|
return result;
|
|
178
182
|
},
|
|
179
183
|
get_index_status: async (_args, ctx) => {
|
|
184
|
+
// Return cached result if still valid
|
|
185
|
+
if (_statusCache && Date.now() < _statusCache.expiresAt) {
|
|
186
|
+
const remainingMin = Math.round((_statusCache.expiresAt - Date.now()) / 60000);
|
|
187
|
+
return _statusCache.data + `\n_Cached (expires in ${remainingMin}min)_`;
|
|
188
|
+
}
|
|
180
189
|
const response = await ctx.api.get(`/api/index/status/${ctx.collectionPrefix}codebase`);
|
|
181
190
|
const data = response.data;
|
|
182
191
|
let result = `## Index Status: ${projectName}\n\n`;
|
|
@@ -185,6 +194,8 @@ export function createIndexingTools(projectName) {
|
|
|
185
194
|
result += `- **Indexed Files:** ${data.indexedFiles ?? "N/A"}\n`;
|
|
186
195
|
result += `- **Last Updated:** ${data.lastUpdated ? new Date(data.lastUpdated).toLocaleString() : "Never"}\n`;
|
|
187
196
|
result += `- **Vector Count:** ${data.vectorCount ?? "N/A"}\n`;
|
|
197
|
+
// Cache for 30 minutes
|
|
198
|
+
_statusCache = { data: result, expiresAt: Date.now() + STATUS_CACHE_TTL };
|
|
188
199
|
return result;
|
|
189
200
|
},
|
|
190
201
|
reindex_zero_downtime: async (args, ctx) => {
|
|
@@ -195,6 +206,7 @@ export function createIndexingTools(projectName) {
|
|
|
195
206
|
excludePatterns,
|
|
196
207
|
force: true,
|
|
197
208
|
});
|
|
209
|
+
_statusCache = null; // Invalidate status cache after reindex
|
|
198
210
|
let result = `## Reindex: ${projectName}\n\n`;
|
|
199
211
|
result += `- **Total files found:** ${stats.totalFiles}\n`;
|
|
200
212
|
result += `- **Files indexed:** ${stats.indexedFiles}\n`;
|
|
@@ -2,12 +2,33 @@
|
|
|
2
2
|
* Suggestions tools module - contextual suggestions, related code,
|
|
3
3
|
* implementation suggestions, test suggestions, and code context.
|
|
4
4
|
*/
|
|
5
|
+
import * as fs from "fs";
|
|
6
|
+
import * as path from "path";
|
|
5
7
|
import { truncate, pct, PREVIEW } from "../formatters.js";
|
|
6
8
|
/**
|
|
7
9
|
* Create the suggestions tools module with project-specific descriptions.
|
|
8
10
|
*/
|
|
9
11
|
export function createSuggestionTools(projectName) {
|
|
10
12
|
const tools = [
|
|
13
|
+
{
|
|
14
|
+
name: "context_briefing",
|
|
15
|
+
description: `REQUIRED before code changes. Parallel lookup of recall + search + patterns + ADRs + graph for ${projectName}. One call replaces 5 separate RAG lookups.`,
|
|
16
|
+
inputSchema: {
|
|
17
|
+
type: "object",
|
|
18
|
+
properties: {
|
|
19
|
+
task: {
|
|
20
|
+
type: "string",
|
|
21
|
+
description: "What you will implement/change",
|
|
22
|
+
},
|
|
23
|
+
files: {
|
|
24
|
+
type: "array",
|
|
25
|
+
items: { type: "string" },
|
|
26
|
+
description: "Files you plan to modify",
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
required: ["task"],
|
|
30
|
+
},
|
|
31
|
+
},
|
|
11
32
|
{
|
|
12
33
|
name: "get_contextual_suggestions",
|
|
13
34
|
description: `Get contextual suggestions based on current work context for ${projectName}. Returns relevant suggestions, triggers, and related memories.`,
|
|
@@ -116,8 +137,153 @@ export function createSuggestionTools(projectName) {
|
|
|
116
137
|
},
|
|
117
138
|
},
|
|
118
139
|
},
|
|
140
|
+
{
|
|
141
|
+
name: "setup_project",
|
|
142
|
+
description: "Configure Claude Code for RAG integration. Creates/updates .mcp.json, adds RAG instructions to CLAUDE.md, and configures permissions. Call after index_codebase on a new project.",
|
|
143
|
+
inputSchema: {
|
|
144
|
+
type: "object",
|
|
145
|
+
properties: {
|
|
146
|
+
projectPath: {
|
|
147
|
+
type: "string",
|
|
148
|
+
description: "Absolute path to project root",
|
|
149
|
+
},
|
|
150
|
+
projectName: {
|
|
151
|
+
type: "string",
|
|
152
|
+
description: "Project name in Qdrant (collection prefix)",
|
|
153
|
+
},
|
|
154
|
+
ragApiUrl: {
|
|
155
|
+
type: "string",
|
|
156
|
+
description: "RAG API URL (default: from MCP env)",
|
|
157
|
+
},
|
|
158
|
+
ragApiKey: {
|
|
159
|
+
type: "string",
|
|
160
|
+
description: "RAG API key (default: from MCP env)",
|
|
161
|
+
},
|
|
162
|
+
updateClaudeMd: {
|
|
163
|
+
type: "boolean",
|
|
164
|
+
description: "Add RAG section to CLAUDE.md (default: true)",
|
|
165
|
+
},
|
|
166
|
+
},
|
|
167
|
+
required: ["projectPath", "projectName"],
|
|
168
|
+
},
|
|
169
|
+
},
|
|
119
170
|
];
|
|
120
171
|
const handlers = {
|
|
172
|
+
context_briefing: async (args, ctx) => {
|
|
173
|
+
const { task, files } = args;
|
|
174
|
+
// 5 parallel lookups
|
|
175
|
+
const [memoriesRes, searchRes, patternsRes, adrsRes, graphRes] = await Promise.all([
|
|
176
|
+
// 1. Recall relevant memories
|
|
177
|
+
ctx.api
|
|
178
|
+
.post("/api/memory/recall", {
|
|
179
|
+
projectName: ctx.projectName,
|
|
180
|
+
query: task,
|
|
181
|
+
limit: 5,
|
|
182
|
+
type: "all",
|
|
183
|
+
})
|
|
184
|
+
.catch(() => null),
|
|
185
|
+
// 2. Hybrid search for related code
|
|
186
|
+
ctx.api
|
|
187
|
+
.post("/api/search-hybrid", {
|
|
188
|
+
projectName: ctx.projectName,
|
|
189
|
+
query: task,
|
|
190
|
+
limit: 5,
|
|
191
|
+
mode: "navigate",
|
|
192
|
+
})
|
|
193
|
+
.catch(() => null),
|
|
194
|
+
// 3. Architectural patterns
|
|
195
|
+
ctx.api
|
|
196
|
+
.post("/api/memory/recall", {
|
|
197
|
+
projectName: ctx.projectName,
|
|
198
|
+
query: task,
|
|
199
|
+
type: "context",
|
|
200
|
+
limit: 5,
|
|
201
|
+
tag: "pattern",
|
|
202
|
+
})
|
|
203
|
+
.catch(() => null),
|
|
204
|
+
// 4. ADRs
|
|
205
|
+
ctx.api
|
|
206
|
+
.post("/api/memory/recall", {
|
|
207
|
+
projectName: ctx.projectName,
|
|
208
|
+
query: task,
|
|
209
|
+
type: "decision",
|
|
210
|
+
limit: 3,
|
|
211
|
+
tag: "adr",
|
|
212
|
+
})
|
|
213
|
+
.catch(() => null),
|
|
214
|
+
// 5. Graph dependencies (if files specified)
|
|
215
|
+
files && files.length > 0
|
|
216
|
+
? ctx.api
|
|
217
|
+
.post("/api/search-graph", {
|
|
218
|
+
projectName: ctx.projectName,
|
|
219
|
+
query: files[0],
|
|
220
|
+
expandHops: 1,
|
|
221
|
+
limit: 5,
|
|
222
|
+
})
|
|
223
|
+
.catch(() => null)
|
|
224
|
+
: Promise.resolve(null),
|
|
225
|
+
]);
|
|
226
|
+
let result = `# Context Briefing: ${task}\n\n`;
|
|
227
|
+
// Memories
|
|
228
|
+
const memories = memoriesRes?.data?.results || memoriesRes?.data?.memories || [];
|
|
229
|
+
if (memories.length > 0) {
|
|
230
|
+
result += `## Memories (${memories.length})\n`;
|
|
231
|
+
for (const m of memories) {
|
|
232
|
+
const mem = m.memory || m;
|
|
233
|
+
result += `- [${mem.type || "note"}] ${truncate(mem.content || "", 150)}\n`;
|
|
234
|
+
}
|
|
235
|
+
result += "\n";
|
|
236
|
+
}
|
|
237
|
+
// Related code
|
|
238
|
+
const codeResults = searchRes?.data?.results || [];
|
|
239
|
+
if (codeResults.length > 0) {
|
|
240
|
+
result += `## Related Code (${codeResults.length})\n`;
|
|
241
|
+
for (const r of codeResults) {
|
|
242
|
+
result += `- \`${r.file}\``;
|
|
243
|
+
if (r.symbols?.length)
|
|
244
|
+
result += ` — ${r.symbols.join(", ")}`;
|
|
245
|
+
result += "\n";
|
|
246
|
+
}
|
|
247
|
+
result += "\n";
|
|
248
|
+
}
|
|
249
|
+
// Patterns
|
|
250
|
+
const patterns = (patternsRes?.data?.results || []).filter((r) => r.memory?.tags?.includes("pattern"));
|
|
251
|
+
if (patterns.length > 0) {
|
|
252
|
+
result += `## Patterns (${patterns.length})\n`;
|
|
253
|
+
for (const p of patterns) {
|
|
254
|
+
const name = p.memory?.metadata?.patternName || p.memory?.relatedTo || "Pattern";
|
|
255
|
+
result += `- **${name}**: ${truncate(p.memory?.content || "", 120)}\n`;
|
|
256
|
+
}
|
|
257
|
+
result += "\n";
|
|
258
|
+
}
|
|
259
|
+
// ADRs
|
|
260
|
+
const adrs = (adrsRes?.data?.results || []).filter((r) => r.memory?.tags?.includes("adr"));
|
|
261
|
+
if (adrs.length > 0) {
|
|
262
|
+
result += `## ADRs (${adrs.length})\n`;
|
|
263
|
+
for (const a of adrs) {
|
|
264
|
+
const title = a.memory?.metadata?.adrTitle || a.memory?.relatedTo || "ADR";
|
|
265
|
+
result += `- **${title}**: ${truncate(a.memory?.content || "", 120)}\n`;
|
|
266
|
+
}
|
|
267
|
+
result += "\n";
|
|
268
|
+
}
|
|
269
|
+
// Graph dependencies
|
|
270
|
+
const graphResults = graphRes?.data?.results || graphRes?.data?.directResults || [];
|
|
271
|
+
const connectedFiles = graphRes?.data?.connectedFiles || graphRes?.data?.expandedResults || [];
|
|
272
|
+
if (graphResults.length > 0 || connectedFiles.length > 0) {
|
|
273
|
+
result += `## Dependencies\n`;
|
|
274
|
+
for (const g of graphResults) {
|
|
275
|
+
result += `- \`${g.file}\`\n`;
|
|
276
|
+
}
|
|
277
|
+
for (const c of connectedFiles) {
|
|
278
|
+
result += `- \`${c.file}\` (connected)\n`;
|
|
279
|
+
}
|
|
280
|
+
result += "\n";
|
|
281
|
+
}
|
|
282
|
+
if (result.endsWith(`# Context Briefing: ${task}\n\n`)) {
|
|
283
|
+
result += "_No relevant context found. Proceed with implementation._\n";
|
|
284
|
+
}
|
|
285
|
+
return result;
|
|
286
|
+
},
|
|
121
287
|
get_contextual_suggestions: async (args, ctx) => {
|
|
122
288
|
const { currentFile, currentCode, recentFiles, task } = args;
|
|
123
289
|
const response = await ctx.api.post("/api/suggestions", {
|
|
@@ -296,6 +462,116 @@ export function createSuggestionTools(projectName) {
|
|
|
296
462
|
}
|
|
297
463
|
return result;
|
|
298
464
|
},
|
|
465
|
+
setup_project: async (args, ctx) => {
|
|
466
|
+
const { projectPath, projectName: targetProject, ragApiUrl, ragApiKey, updateClaudeMd = true, } = args;
|
|
467
|
+
const apiUrl = ragApiUrl || process.env.RAG_API_URL || "http://localhost:3100";
|
|
468
|
+
const apiKey = ragApiKey || process.env.RAG_API_KEY;
|
|
469
|
+
const serverName = `${targetProject}-rag`;
|
|
470
|
+
const changes = [];
|
|
471
|
+
// 1. Create/update .mcp.json
|
|
472
|
+
const mcpJsonPath = path.join(projectPath, ".mcp.json");
|
|
473
|
+
let mcpConfig = {};
|
|
474
|
+
try {
|
|
475
|
+
const existing = fs.readFileSync(mcpJsonPath, "utf-8");
|
|
476
|
+
mcpConfig = JSON.parse(existing);
|
|
477
|
+
}
|
|
478
|
+
catch {
|
|
479
|
+
// File doesn't exist or invalid JSON
|
|
480
|
+
}
|
|
481
|
+
if (!mcpConfig.mcpServers)
|
|
482
|
+
mcpConfig.mcpServers = {};
|
|
483
|
+
const serverEnv = {
|
|
484
|
+
RAG_API_URL: apiUrl,
|
|
485
|
+
PROJECT_NAME: targetProject,
|
|
486
|
+
PROJECT_PATH: projectPath,
|
|
487
|
+
};
|
|
488
|
+
if (apiKey)
|
|
489
|
+
serverEnv.RAG_API_KEY = apiKey;
|
|
490
|
+
mcpConfig.mcpServers[serverName] = {
|
|
491
|
+
command: "npx",
|
|
492
|
+
args: ["-y", "@crowley/rag-mcp@latest"],
|
|
493
|
+
env: serverEnv,
|
|
494
|
+
};
|
|
495
|
+
fs.writeFileSync(mcpJsonPath, JSON.stringify(mcpConfig, null, 2) + "\n");
|
|
496
|
+
changes.push(`.mcp.json — added \`${serverName}\` server`);
|
|
497
|
+
// 2. Update CLAUDE.md with RAG section
|
|
498
|
+
if (updateClaudeMd) {
|
|
499
|
+
const claudeMdPath = path.join(projectPath, "CLAUDE.md");
|
|
500
|
+
let claudeMd = "";
|
|
501
|
+
try {
|
|
502
|
+
claudeMd = fs.readFileSync(claudeMdPath, "utf-8");
|
|
503
|
+
}
|
|
504
|
+
catch {
|
|
505
|
+
// File doesn't exist
|
|
506
|
+
}
|
|
507
|
+
const ragSection = `\n## RAG Integration
|
|
508
|
+
|
|
509
|
+
You MUST call \`context_briefing\` before making any code changes.
|
|
510
|
+
This single tool performs all RAG lookups in parallel (recall, search, patterns, ADRs, graph).
|
|
511
|
+
|
|
512
|
+
Example: \`context_briefing(task: "describe your change", files: ["path/to/file.ts"])\`
|
|
513
|
+
|
|
514
|
+
After completing significant changes:
|
|
515
|
+
- \`remember\` — save important context for future sessions
|
|
516
|
+
- \`record_adr\` — document architectural decisions
|
|
517
|
+
`;
|
|
518
|
+
if (claudeMd.includes("## RAG")) {
|
|
519
|
+
changes.push("CLAUDE.md — RAG section already exists, skipped");
|
|
520
|
+
}
|
|
521
|
+
else {
|
|
522
|
+
claudeMd = claudeMd ? claudeMd.trimEnd() + "\n" + ragSection : `# CLAUDE.md\n${ragSection}`;
|
|
523
|
+
fs.writeFileSync(claudeMdPath, claudeMd);
|
|
524
|
+
changes.push("CLAUDE.md — added RAG Integration section");
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
// 3. Create/update .claude/settings.local.json permissions
|
|
528
|
+
const claudeDir = path.join(projectPath, ".claude");
|
|
529
|
+
const settingsPath = path.join(claudeDir, "settings.local.json");
|
|
530
|
+
let settings = {};
|
|
531
|
+
try {
|
|
532
|
+
const existing = fs.readFileSync(settingsPath, "utf-8");
|
|
533
|
+
settings = JSON.parse(existing);
|
|
534
|
+
}
|
|
535
|
+
catch {
|
|
536
|
+
// File doesn't exist or invalid JSON
|
|
537
|
+
}
|
|
538
|
+
if (!settings.permissions)
|
|
539
|
+
settings.permissions = {};
|
|
540
|
+
if (!settings.permissions.allow)
|
|
541
|
+
settings.permissions.allow = [];
|
|
542
|
+
const mcpPermission = `mcp__${serverName}__*`;
|
|
543
|
+
if (!settings.permissions.allow.includes(mcpPermission)) {
|
|
544
|
+
settings.permissions.allow.push(mcpPermission);
|
|
545
|
+
if (!fs.existsSync(claudeDir))
|
|
546
|
+
fs.mkdirSync(claudeDir, { recursive: true });
|
|
547
|
+
fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
548
|
+
changes.push(`.claude/settings.local.json — added \`${mcpPermission}\` permission`);
|
|
549
|
+
}
|
|
550
|
+
else {
|
|
551
|
+
changes.push(".claude/settings.local.json — permission already exists, skipped");
|
|
552
|
+
}
|
|
553
|
+
// 4. Check index status
|
|
554
|
+
let indexInfo = "";
|
|
555
|
+
try {
|
|
556
|
+
const statusRes = await ctx.api.get(`/api/index/status/${targetProject}_codebase`);
|
|
557
|
+
const data = statusRes.data;
|
|
558
|
+
indexInfo = `\n## Index Status\n- **Vectors:** ${data.vectorCount ?? "N/A"}\n- **Status:** ${data.status || "unknown"}\n`;
|
|
559
|
+
}
|
|
560
|
+
catch {
|
|
561
|
+
indexInfo = "\n## Index Status\n_Not indexed yet. Run `index_codebase` first._\n";
|
|
562
|
+
}
|
|
563
|
+
let result = `# Project Setup: ${targetProject}\n\n`;
|
|
564
|
+
result += `## Files Updated\n`;
|
|
565
|
+
for (const c of changes) {
|
|
566
|
+
result += `- ${c}\n`;
|
|
567
|
+
}
|
|
568
|
+
result += indexInfo;
|
|
569
|
+
result += `\n## Next Steps\n`;
|
|
570
|
+
result += `1. Restart Claude Code to load the new MCP server\n`;
|
|
571
|
+
result += `2. Run \`index_codebase\` if not indexed yet\n`;
|
|
572
|
+
result += `3. Use \`context_briefing\` before code changes\n`;
|
|
573
|
+
return result;
|
|
574
|
+
},
|
|
299
575
|
};
|
|
300
576
|
return { tools, handlers };
|
|
301
577
|
}
|