@crowley/rag-mcp 1.0.4 → 1.0.6

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.
Files changed (50) hide show
  1. package/dist/annotations.d.ts +16 -0
  2. package/dist/annotations.js +158 -0
  3. package/dist/context-enrichment.d.ts +2 -2
  4. package/dist/context-enrichment.js +37 -14
  5. package/dist/formatters.d.ts +2 -0
  6. package/dist/formatters.js +12 -0
  7. package/dist/index.js +64 -47
  8. package/dist/schemas.d.ts +97 -0
  9. package/dist/schemas.js +128 -0
  10. package/dist/tool-middleware.d.ts +40 -0
  11. package/dist/tool-middleware.js +216 -0
  12. package/dist/tool-registry.js +2 -1
  13. package/dist/tools/advanced.d.ts +2 -2
  14. package/dist/tools/advanced.js +200 -275
  15. package/dist/tools/agents.d.ts +2 -2
  16. package/dist/tools/agents.js +59 -78
  17. package/dist/tools/analytics.d.ts +2 -2
  18. package/dist/tools/analytics.js +170 -210
  19. package/dist/tools/architecture.d.ts +2 -2
  20. package/dist/tools/architecture.js +506 -661
  21. package/dist/tools/ask.d.ts +2 -2
  22. package/dist/tools/ask.js +164 -219
  23. package/dist/tools/cache.d.ts +2 -2
  24. package/dist/tools/cache.js +63 -82
  25. package/dist/tools/clustering.d.ts +2 -2
  26. package/dist/tools/clustering.js +154 -215
  27. package/dist/tools/confluence.d.ts +2 -2
  28. package/dist/tools/confluence.js +80 -116
  29. package/dist/tools/database.d.ts +2 -2
  30. package/dist/tools/database.js +303 -380
  31. package/dist/tools/feedback.d.ts +2 -2
  32. package/dist/tools/feedback.js +143 -184
  33. package/dist/tools/guidelines.d.ts +2 -2
  34. package/dist/tools/guidelines.js +123 -135
  35. package/dist/tools/indexing.d.ts +2 -2
  36. package/dist/tools/indexing.js +104 -100
  37. package/dist/tools/memory.d.ts +2 -2
  38. package/dist/tools/memory.js +299 -485
  39. package/dist/tools/pm.d.ts +2 -2
  40. package/dist/tools/pm.js +367 -615
  41. package/dist/tools/review.d.ts +2 -2
  42. package/dist/tools/review.js +142 -189
  43. package/dist/tools/search.d.ts +2 -2
  44. package/dist/tools/search.js +230 -305
  45. package/dist/tools/session.d.ts +2 -2
  46. package/dist/tools/session.js +288 -345
  47. package/dist/tools/suggestions.d.ts +2 -2
  48. package/dist/tools/suggestions.js +444 -255
  49. package/dist/types.d.ts +19 -2
  50. package/package.json +4 -2
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Tool Middleware — standalone middleware functions for MCP tool handlers.
3
+ *
4
+ * Extracted from ToolRegistry.handle() so that McpServer.registerTool()
5
+ * (Phase 3) can reuse the same pipeline:
6
+ * auto-session → enrichment.before → handler → enrichment.after → trackUsage
7
+ *
8
+ * During Phase 2 migration, ToolRegistry continues to use its own copy.
9
+ * Phase 3 replaces ToolRegistry with wrapHandler() + McpServer.registerTool().
10
+ */
11
+ import type { ToolContext, ToolHandler } from "./types.js";
12
+ import type { ContextEnricher } from "./context-enrichment.js";
13
+ /** Per-tool timeout overrides (ms) */
14
+ export declare const TOOL_TIMEOUTS: Record<string, number>;
15
+ /** Tools excluded from usage tracking (meta/admin, avoid recursion) */
16
+ export declare const TRACKING_EXCLUDE: Set<string>;
17
+ /** Session management tools — skip auto-session to avoid recursion */
18
+ export declare const SESSION_TOOLS: Set<string>;
19
+ /** Summarize tool args into a short string for analytics */
20
+ export declare function summarizeInput(name: string, args: Record<string, unknown>): string;
21
+ /** Count results from a tool response string */
22
+ export declare function countResults(result: string): number;
23
+ /** Auto-start a session via the RAG API if none is active */
24
+ export declare function ensureSession(ctx: ToolContext): Promise<void>;
25
+ /** Fire-and-forget usage tracking */
26
+ export declare function trackUsage(name: string, args: Record<string, unknown>, startTime: number, success: boolean, result: string, errorMessage: string | undefined, ctx: ToolContext): void;
27
+ /** Format an error caught during tool execution */
28
+ export declare function formatToolError(error: unknown, ctx: ToolContext): string;
29
+ export interface MiddlewareDeps {
30
+ enricher?: ContextEnricher;
31
+ ctx: ToolContext;
32
+ }
33
+ /**
34
+ * Wrap a raw ToolHandler with the full middleware pipeline:
35
+ * auto-session → enrichment.before → handler → enrichment.after → trackUsage
36
+ *
37
+ * Returns a ToolHandler with the same signature, so it works both
38
+ * with the legacy ToolRegistry and the Phase 3 McpServer adapter.
39
+ */
40
+ export declare function wrapHandler(name: string, handler: ToolHandler, deps: MiddlewareDeps): ToolHandler;
@@ -0,0 +1,216 @@
1
+ /**
2
+ * Tool Middleware — standalone middleware functions for MCP tool handlers.
3
+ *
4
+ * Extracted from ToolRegistry.handle() so that McpServer.registerTool()
5
+ * (Phase 3) can reuse the same pipeline:
6
+ * auto-session → enrichment.before → handler → enrichment.after → trackUsage
7
+ *
8
+ * During Phase 2 migration, ToolRegistry continues to use its own copy.
9
+ * Phase 3 replaces ToolRegistry with wrapHandler() + McpServer.registerTool().
10
+ */
11
+ // ── Timeouts ────────────────────────────────────────────────
12
+ /** Default tool timeout in milliseconds */
13
+ const DEFAULT_TIMEOUT_MS = 30_000;
14
+ /** Per-tool timeout overrides (ms) */
15
+ export const TOOL_TIMEOUTS = {
16
+ // Indexing / heavy analysis — up to 2 min
17
+ index_codebase: 120_000,
18
+ reindex_zero_downtime: 120_000,
19
+ cluster_code: 60_000,
20
+ find_duplicates: 60_000,
21
+ run_quality_gates: 60_000,
22
+ analyze_project_structure: 60_000,
23
+ estimate_feature: 60_000,
24
+ // Quick search — 15 s
25
+ search_codebase: 15_000,
26
+ hybrid_search: 15_000,
27
+ search_similar: 15_000,
28
+ search_graph: 15_000,
29
+ grouped_search: 15_000,
30
+ search_docs: 10_000,
31
+ find_symbol: 10_000,
32
+ // Memory / recall — 10 s
33
+ recall: 10_000,
34
+ remember: 10_000,
35
+ context_briefing: 15_000,
36
+ };
37
+ /** Race a promise against a timeout */
38
+ async function withTimeout(promise, ms, name) {
39
+ let timer;
40
+ const timeout = new Promise((_, reject) => {
41
+ timer = setTimeout(() => reject(new Error(`Tool '${name}' timed out after ${ms}ms`)), ms);
42
+ });
43
+ try {
44
+ return await Promise.race([promise, timeout]);
45
+ }
46
+ finally {
47
+ clearTimeout(timer);
48
+ }
49
+ }
50
+ // ── Constants ───────────────────────────────────────────────
51
+ /** Tools excluded from usage tracking (meta/admin, avoid recursion) */
52
+ export const TRACKING_EXCLUDE = new Set([
53
+ "get_tool_analytics",
54
+ "get_knowledge_gaps",
55
+ "analyze_usage_patterns",
56
+ "get_behavior_patterns",
57
+ "get_quality_metrics",
58
+ "get_cache_stats",
59
+ "get_prediction_stats",
60
+ "get_rag_guidelines",
61
+ ]);
62
+ /** Session management tools — skip auto-session to avoid recursion */
63
+ export const SESSION_TOOLS = new Set([
64
+ "start_session",
65
+ "end_session",
66
+ "get_session_context",
67
+ ]);
68
+ // ── Helpers ─────────────────────────────────────────────────
69
+ /** Summarize tool args into a short string for analytics */
70
+ export function summarizeInput(name, args) {
71
+ const q = args.query || args.question || args.feature || args.description || args.task || "";
72
+ if (q && typeof q === "string")
73
+ return q.slice(0, 200);
74
+ const content = args.content || args.code || args.diff || "";
75
+ if (content && typeof content === "string")
76
+ return content.slice(0, 100);
77
+ const file = args.file || args.filePath || args.currentFile || "";
78
+ if (file && typeof file === "string")
79
+ return file;
80
+ for (const v of Object.values(args)) {
81
+ if (typeof v === "string" && v.length > 0)
82
+ return v.slice(0, 150);
83
+ }
84
+ return name;
85
+ }
86
+ /** Count results from a tool response string */
87
+ export function countResults(result) {
88
+ if (result.includes("No results") ||
89
+ result.includes("No matches") ||
90
+ result.includes("not found"))
91
+ return 0;
92
+ const numbered = result.match(/^\d+\./gm);
93
+ if (numbered)
94
+ return numbered.length;
95
+ const bullets = result.match(/^[-*] /gm);
96
+ if (bullets)
97
+ return bullets.length;
98
+ return 1;
99
+ }
100
+ // ── Session management ──────────────────────────────────────
101
+ /** Shared lock to prevent concurrent auto-session starts */
102
+ let autoSessionPromise = null;
103
+ /** Auto-start a session via the RAG API if none is active */
104
+ export async function ensureSession(ctx) {
105
+ if (ctx.activeSessionId)
106
+ return;
107
+ if (autoSessionPromise) {
108
+ await autoSessionPromise;
109
+ return;
110
+ }
111
+ autoSessionPromise = doAutoStartSession(ctx);
112
+ try {
113
+ await autoSessionPromise;
114
+ }
115
+ finally {
116
+ autoSessionPromise = null;
117
+ }
118
+ }
119
+ async function doAutoStartSession(ctx) {
120
+ try {
121
+ const response = await ctx.api.post("/api/session/start", {
122
+ projectName: ctx.projectName,
123
+ initialContext: "auto-started by MCP tool call",
124
+ });
125
+ const session = response.data?.session;
126
+ const sid = session?.sessionId || response.data?.sessionId;
127
+ if (sid) {
128
+ ctx.activeSessionId = sid;
129
+ }
130
+ // Fire-and-forget: ensure critical collections exist
131
+ ctx.api
132
+ .post("/api/ensure-collections", { projectName: ctx.projectName })
133
+ .catch(() => { });
134
+ }
135
+ catch {
136
+ // Silent — auto-session must never block tool execution
137
+ }
138
+ }
139
+ // ── Usage tracking ──────────────────────────────────────────
140
+ /** Fire-and-forget usage tracking */
141
+ export function trackUsage(name, args, startTime, success, result, errorMessage, ctx) {
142
+ if (TRACKING_EXCLUDE.has(name))
143
+ return;
144
+ ctx.api
145
+ .post("/api/track-usage", {
146
+ projectName: ctx.projectName,
147
+ sessionId: ctx.activeSessionId,
148
+ toolName: name,
149
+ inputSummary: summarizeInput(name, args),
150
+ startTime,
151
+ resultCount: success ? countResults(result) : 0,
152
+ success,
153
+ errorMessage,
154
+ })
155
+ .catch(() => { });
156
+ }
157
+ // ── Error formatting ────────────────────────────────────────
158
+ /** Format an error caught during tool execution */
159
+ export function formatToolError(error, ctx) {
160
+ const err = error;
161
+ if (err.code === "ECONNREFUSED") {
162
+ return (`Error: Cannot connect to RAG API at ${ctx.api.defaults.baseURL}. Is it running?\n` +
163
+ `Start with: cd docker && docker-compose up -d`);
164
+ }
165
+ if (err.response) {
166
+ return `API Error (${err.response.status}): ${JSON.stringify(err.response.data)}`;
167
+ }
168
+ return `Error: ${err.message || String(error)}`;
169
+ }
170
+ /**
171
+ * Wrap a raw ToolHandler with the full middleware pipeline:
172
+ * auto-session → enrichment.before → handler → enrichment.after → trackUsage
173
+ *
174
+ * Returns a ToolHandler with the same signature, so it works both
175
+ * with the legacy ToolRegistry and the Phase 3 McpServer adapter.
176
+ */
177
+ export function wrapHandler(name, handler, deps) {
178
+ return async (args, ctx) => {
179
+ // Auto-start session (skip for session management tools)
180
+ if (!SESSION_TOOLS.has(name)) {
181
+ await ensureSession(ctx);
182
+ }
183
+ const startTime = Date.now();
184
+ try {
185
+ // Before: auto-enrich context
186
+ const contextPrefix = ctx.enrichmentEnabled && deps.enricher
187
+ ? await deps.enricher.before(name, args, ctx)
188
+ : null;
189
+ // Execute original handler (with timeout)
190
+ const timeoutMs = TOOL_TIMEOUTS[name] ?? DEFAULT_TIMEOUT_MS;
191
+ const result = await withTimeout(handler(args, ctx), timeoutMs, name);
192
+ // Extract text for tracking/enrichment
193
+ const text = typeof result === "string" ? result : result.text;
194
+ // After: track interaction (fire-and-forget)
195
+ if (deps.enricher) {
196
+ deps.enricher.after(name, args, text, ctx);
197
+ }
198
+ // Track usage (fire-and-forget)
199
+ trackUsage(name, args, startTime, true, text, undefined, ctx);
200
+ // Prepend context if available
201
+ if (contextPrefix) {
202
+ if (typeof result === "string") {
203
+ return contextPrefix + "\n\n" + result;
204
+ }
205
+ return { text: contextPrefix + "\n\n" + result.text, structured: result.structured };
206
+ }
207
+ return result;
208
+ }
209
+ catch (error) {
210
+ const errorMessage = formatToolError(error, ctx);
211
+ // Track failed usage (fire-and-forget)
212
+ trackUsage(name, args, startTime, false, "", errorMessage, ctx);
213
+ return errorMessage;
214
+ }
215
+ };
216
+ }
@@ -149,7 +149,8 @@ export class ToolRegistry {
149
149
  ? await this.enricher.before(name, args, ctx)
150
150
  : null;
151
151
  // Execute original handler
152
- const result = await handler(args, ctx);
152
+ const rawResult = await handler(args, ctx);
153
+ const result = typeof rawResult === "string" ? rawResult : rawResult.text;
153
154
  // After: track interaction (fire-and-forget)
154
155
  if (this.enricher) {
155
156
  this.enricher.after(name, args, result, ctx);
@@ -2,8 +2,8 @@
2
2
  * Advanced tools module - memory merging, code completion context,
3
3
  * import suggestions, type context, and behavior patterns.
4
4
  */
5
- import type { ToolModule } from "../types.js";
5
+ import type { ToolSpec } from "../types.js";
6
6
  /**
7
7
  * Create the advanced tools module with project-specific descriptions.
8
8
  */
9
- export declare function createAdvancedTools(projectName: string): ToolModule;
9
+ export declare function createAdvancedTools(projectName: string): ToolSpec[];