@crowley/rag-mcp 1.0.6 → 1.1.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,6 +34,7 @@ import { createCacheTools } from "./tools/cache.js";
34
34
  import { createGuidelinesTools } from "./tools/guidelines.js";
35
35
  import { createAdvancedTools } from "./tools/advanced.js";
36
36
  import { createAgentTools } from "./tools/agents.js";
37
+ import { createQualityTools } from "./tools/quality.js";
37
38
  // Configuration from environment
38
39
  const PROJECT_NAME = process.env.PROJECT_NAME || "default";
39
40
  const PROJECT_PATH = process.env.PROJECT_PATH || process.cwd();
@@ -76,11 +77,63 @@ const allSpecs = [
76
77
  ...createGuidelinesTools(PROJECT_NAME),
77
78
  ...createAdvancedTools(PROJECT_NAME),
78
79
  ...createAgentTools(PROJECT_NAME),
80
+ ...createQualityTools(PROJECT_NAME),
79
81
  ];
82
+ // Core tools exposed directly to Claude (~35 tools).
83
+ // Hidden tools remain accessible via run_agent (agent runtime calls API directly).
84
+ const CORE_TOOLS = new Set([
85
+ // Search (6)
86
+ "search_codebase",
87
+ "hybrid_search",
88
+ "search_graph",
89
+ "find_symbol",
90
+ "search_docs",
91
+ "find_feature",
92
+ // Ask (2)
93
+ "ask_codebase",
94
+ "explain_code",
95
+ // Index (3)
96
+ "index_codebase",
97
+ "get_index_status",
98
+ "get_project_stats",
99
+ // Memory (7)
100
+ "remember",
101
+ "recall",
102
+ "list_memories",
103
+ "forget",
104
+ "batch_remember",
105
+ "promote_memory",
106
+ "review_memories",
107
+ // Architecture (6)
108
+ "record_adr",
109
+ "get_adrs",
110
+ "record_pattern",
111
+ "get_patterns",
112
+ "record_tech_debt",
113
+ "get_tech_debt",
114
+ // Context (3)
115
+ "context_briefing",
116
+ "smart_dispatch",
117
+ "setup_project",
118
+ // Session (2)
119
+ "start_session",
120
+ "end_session",
121
+ // Confluence (2)
122
+ "search_confluence",
123
+ "index_confluence",
124
+ // DB (4)
125
+ "record_table",
126
+ "get_table_info",
127
+ "check_db_schema",
128
+ "get_db_rules",
129
+ // Agents (1)
130
+ "run_agent",
131
+ ]);
132
+ const coreSpecs = allSpecs.filter((s) => CORE_TOOLS.has(s.name));
80
133
  // MCP Server (modern McpServer API with native Zod validation)
81
- const server = new McpServer({ name: `${PROJECT_NAME}-rag`, version: "1.0.5" }, { capabilities: { tools: {} } });
82
- // Register all tools with McpServer using wrapHandler middleware
83
- for (const spec of allSpecs) {
134
+ const server = new McpServer({ name: `${PROJECT_NAME}-rag`, version: "1.1.0" }, { capabilities: { tools: {} } });
135
+ // Register core tools with McpServer using wrapHandler middleware
136
+ for (const spec of coreSpecs) {
84
137
  const wrapped = wrapHandler(spec.name, spec.handler, { enricher, ctx });
85
138
  server.registerTool(spec.name, {
86
139
  description: spec.description,
@@ -121,6 +174,6 @@ async function main() {
121
174
  const transport = new StdioServerTransport();
122
175
  await server.connect(transport);
123
176
  console.error(`${PROJECT_NAME} RAG MCP server running (collection prefix: ${COLLECTION_PREFIX})`);
124
- console.error(`Registered ${allSpecs.length} tools from 18 modules`);
177
+ console.error(`Registered ${coreSpecs.length}/${allSpecs.length} core tools (${allSpecs.length - coreSpecs.length} hidden, accessible via run_agent)`);
125
178
  }
126
179
  main().catch(console.error);
package/dist/schemas.d.ts CHANGED
@@ -83,13 +83,13 @@ export declare const MemoryRecordInput: z.ZodObject<{
83
83
  importance: z.ZodOptional<z.ZodDefault<z.ZodEnum<["low", "medium", "high", "critical"]>>>;
84
84
  context: z.ZodOptional<z.ZodString>;
85
85
  }, "strip", z.ZodTypeAny, {
86
- type: "decision" | "insight" | "todo" | "adr" | "pattern" | "tech_debt" | "architecture" | "convention" | "bug_fix" | "optimization";
86
+ type: "decision" | "insight" | "todo" | "adr" | "pattern" | "architecture" | "tech_debt" | "convention" | "bug_fix" | "optimization";
87
87
  content: string;
88
88
  context?: string | undefined;
89
89
  tags?: string[] | undefined;
90
90
  importance?: "low" | "medium" | "high" | "critical" | undefined;
91
91
  }, {
92
- type: "decision" | "insight" | "todo" | "adr" | "pattern" | "tech_debt" | "architecture" | "convention" | "bug_fix" | "optimization";
92
+ type: "decision" | "insight" | "todo" | "adr" | "pattern" | "architecture" | "tech_debt" | "convention" | "bug_fix" | "optimization";
93
93
  content: string;
94
94
  context?: string | undefined;
95
95
  tags?: string[] | undefined;
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Quality tools module - LLM quality monitoring and reporting.
3
+ */
4
+ import type { ToolSpec } from "../types.js";
5
+ /**
6
+ * Create the quality tools module.
7
+ */
8
+ export declare function createQualityTools(projectName: string): ToolSpec[];
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Quality tools module - LLM quality monitoring and reporting.
3
+ */
4
+ import { z } from "zod";
5
+ import { TOOL_ANNOTATIONS } from "../annotations.js";
6
+ /**
7
+ * Create the quality tools module.
8
+ */
9
+ export function createQualityTools(projectName) {
10
+ return [
11
+ {
12
+ name: "get_quality_report",
13
+ description: `Get LLM quality metrics for ${projectName}. Shows JSON parse rates, latency percentiles, thinking trace rates, and alerts.`,
14
+ schema: z.object({
15
+ endpoint: z.string().optional().describe("Filter by specific endpoint (e.g., '/api/ask')"),
16
+ }),
17
+ annotations: TOOL_ANNOTATIONS["get_quality_report"] || {
18
+ title: "Get Quality Report",
19
+ readOnlyHint: true,
20
+ openWorldHint: false,
21
+ },
22
+ handler: async (args, ctx) => {
23
+ const params = args.endpoint ? `?endpoint=${encodeURIComponent(args.endpoint)}` : '';
24
+ const response = await ctx.api.get(`/api/quality/report${params}`);
25
+ const data = response.data;
26
+ let result = `## Quality Report\n\n`;
27
+ result += `**Total Metrics:** ${data.total}\n\n`;
28
+ if (data.total === 0) {
29
+ result += `No quality metrics recorded yet.\n`;
30
+ return result;
31
+ }
32
+ const m = data.metrics;
33
+ result += `### Aggregate Metrics\n`;
34
+ result += `- **Avg Latency:** ${m.avgLatencyMs}ms\n`;
35
+ result += `- **P95 Latency:** ${m.p95LatencyMs}ms\n`;
36
+ result += `- **JSON Parse Rate:** ${(m.jsonParseRate * 100).toFixed(1)}%\n`;
37
+ result += `- **Thinking Rate:** ${(m.thinkingRate * 100).toFixed(1)}%\n`;
38
+ result += `- **Avg Output Length:** ${m.avgOutputLength} chars\n`;
39
+ result += `- **Avg Thinking Length:** ${m.avgThinkingLength} chars\n`;
40
+ result += `- **Avg Tokens:** ${m.avgTokens}\n\n`;
41
+ if (data.alerts.length > 0) {
42
+ result += `### ⚠ Alerts\n`;
43
+ for (const alert of data.alerts) {
44
+ result += `- ${alert}\n`;
45
+ }
46
+ result += `\n`;
47
+ }
48
+ if (Object.keys(data.byEndpoint).length > 0) {
49
+ result += `### By Endpoint\n`;
50
+ for (const [ep, stats] of Object.entries(data.byEndpoint)) {
51
+ result += `- **${ep}**: ${stats.count} calls, ${stats.avgLatencyMs}ms avg, `;
52
+ result += `JSON: ${(stats.jsonParseRate * 100).toFixed(0)}%, `;
53
+ result += `Thinking: ${(stats.thinkingRate * 100).toFixed(0)}%\n`;
54
+ }
55
+ }
56
+ return result;
57
+ },
58
+ },
59
+ ];
60
+ }
@@ -7,6 +7,75 @@ import * as path from "path";
7
7
  import { truncate, pct, PREVIEW } from "../formatters.js";
8
8
  import { z } from "zod";
9
9
  import { TOOL_ANNOTATIONS } from "../annotations.js";
10
+ /**
11
+ * Format smart dispatch result into readable markdown.
12
+ */
13
+ function formatSmartDispatchResult(task, data) {
14
+ let result = `# Context Briefing: ${task}\n`;
15
+ result += `_Routing: ${data.reasoning} (${data.plan?.join(", ")}) [${data.timing?.totalMs}ms]_\n\n`;
16
+ const ctx = data.context || {};
17
+ if (ctx.memories?.length > 0) {
18
+ result += `## Memories (${ctx.memories.length})\n`;
19
+ for (const m of ctx.memories) {
20
+ const mem = m.memory || m;
21
+ result += `- [${mem.type || "note"}] ${(mem.content || "").slice(0, 150)}\n`;
22
+ }
23
+ result += "\n";
24
+ }
25
+ if (ctx.codeResults?.length > 0) {
26
+ result += `## Related Code (${ctx.codeResults.length})\n`;
27
+ for (const r of ctx.codeResults) {
28
+ result += `- \`${r.file}\``;
29
+ if (r.symbols?.length)
30
+ result += ` — ${r.symbols.join(", ")}`;
31
+ result += "\n";
32
+ }
33
+ result += "\n";
34
+ }
35
+ if (ctx.patterns?.length > 0) {
36
+ result += `## Patterns (${ctx.patterns.length})\n`;
37
+ for (const p of ctx.patterns) {
38
+ const mem = p.memory || p;
39
+ const name = mem.metadata?.patternName || mem.relatedTo || "Pattern";
40
+ result += `- **${name}**: ${(mem.content || "").slice(0, 120)}\n`;
41
+ }
42
+ result += "\n";
43
+ }
44
+ if (ctx.adrs?.length > 0) {
45
+ result += `## ADRs (${ctx.adrs.length})\n`;
46
+ for (const a of ctx.adrs) {
47
+ const mem = a.memory || a;
48
+ const title = mem.metadata?.adrTitle || mem.relatedTo || "ADR";
49
+ result += `- **${title}**: ${(mem.content || "").slice(0, 120)}\n`;
50
+ }
51
+ result += "\n";
52
+ }
53
+ if (ctx.graphDeps?.length > 0) {
54
+ result += `## Dependencies (${ctx.graphDeps.length})\n`;
55
+ for (const g of ctx.graphDeps) {
56
+ result += `- \`${g.file}\`\n`;
57
+ }
58
+ result += "\n";
59
+ }
60
+ if (ctx.docs?.length > 0) {
61
+ result += `## Docs (${ctx.docs.length})\n`;
62
+ for (const d of ctx.docs) {
63
+ result += `- \`${d.file}\`: ${(d.content || "").slice(0, 100)}\n`;
64
+ }
65
+ result += "\n";
66
+ }
67
+ if (ctx.symbols?.length > 0) {
68
+ result += `## Symbols (${ctx.symbols.length})\n`;
69
+ for (const s of ctx.symbols) {
70
+ result += `- \`${s.name || s.symbol}\` [${s.kind || "unknown"}] in \`${s.file || "?"}\`\n`;
71
+ }
72
+ result += "\n";
73
+ }
74
+ if (result.endsWith(`_Routing: ${data.reasoning} (${data.plan?.join(", ")}) [${data.timing?.totalMs}ms]_\n\n`)) {
75
+ result += "_No relevant context found. Proceed with implementation._\n";
76
+ }
77
+ return result;
78
+ }
10
79
  /**
11
80
  * Create the suggestions tools module with project-specific descriptions.
12
81
  */
@@ -22,9 +91,21 @@ export function createSuggestionTools(projectName) {
22
91
  annotations: TOOL_ANNOTATIONS["context_briefing"],
23
92
  handler: async (args, ctx) => {
24
93
  const { task, files } = args;
25
- // 5 parallel lookups
94
+ // Use smart_dispatch for intelligent routing
95
+ try {
96
+ const dispatchRes = await ctx.api.post("/api/smart-dispatch", {
97
+ projectName: ctx.projectName,
98
+ task,
99
+ files,
100
+ });
101
+ const data = dispatchRes.data;
102
+ return formatSmartDispatchResult(task, data);
103
+ }
104
+ catch {
105
+ // Fallback to legacy 5-parallel-lookups if smart-dispatch unavailable
106
+ }
107
+ // Legacy fallback: 5 parallel lookups
26
108
  const [memoriesRes, searchRes, patternsRes, adrsRes, graphRes] = await Promise.all([
27
- // 1. Recall relevant memories
28
109
  ctx.api
29
110
  .post("/api/memory/recall", {
30
111
  projectName: ctx.projectName,
@@ -33,7 +114,6 @@ export function createSuggestionTools(projectName) {
33
114
  type: "all",
34
115
  })
35
116
  .catch(() => null),
36
- // 2. Hybrid search for related code
37
117
  ctx.api
38
118
  .post("/api/search-hybrid", {
39
119
  projectName: ctx.projectName,
@@ -42,7 +122,6 @@ export function createSuggestionTools(projectName) {
42
122
  mode: "navigate",
43
123
  })
44
124
  .catch(() => null),
45
- // 3. Architectural patterns
46
125
  ctx.api
47
126
  .post("/api/memory/recall", {
48
127
  projectName: ctx.projectName,
@@ -52,7 +131,6 @@ export function createSuggestionTools(projectName) {
52
131
  tag: "pattern",
53
132
  })
54
133
  .catch(() => null),
55
- // 4. ADRs
56
134
  ctx.api
57
135
  .post("/api/memory/recall", {
58
136
  projectName: ctx.projectName,
@@ -62,7 +140,6 @@ export function createSuggestionTools(projectName) {
62
140
  tag: "adr",
63
141
  })
64
142
  .catch(() => null),
65
- // 5. Graph dependencies (if files specified)
66
143
  files && files.length > 0
67
144
  ? ctx.api
68
145
  .post("/api/search-graph", {
@@ -75,7 +152,6 @@ export function createSuggestionTools(projectName) {
75
152
  : Promise.resolve(null),
76
153
  ]);
77
154
  let result = `# Context Briefing: ${task}\n\n`;
78
- // Memories
79
155
  const memories = memoriesRes?.data?.results || memoriesRes?.data?.memories || [];
80
156
  if (memories.length > 0) {
81
157
  result += `## Memories (${memories.length})\n`;
@@ -85,7 +161,6 @@ export function createSuggestionTools(projectName) {
85
161
  }
86
162
  result += "\n";
87
163
  }
88
- // Related code
89
164
  const codeResults = searchRes?.data?.results || [];
90
165
  if (codeResults.length > 0) {
91
166
  result += `## Related Code (${codeResults.length})\n`;
@@ -97,7 +172,6 @@ export function createSuggestionTools(projectName) {
97
172
  }
98
173
  result += "\n";
99
174
  }
100
- // Patterns
101
175
  const patterns = (patternsRes?.data?.results || []).filter((r) => r.memory?.tags?.includes("pattern"));
102
176
  if (patterns.length > 0) {
103
177
  result += `## Patterns (${patterns.length})\n`;
@@ -107,7 +181,6 @@ export function createSuggestionTools(projectName) {
107
181
  }
108
182
  result += "\n";
109
183
  }
110
- // ADRs
111
184
  const adrs = (adrsRes?.data?.results || []).filter((r) => r.memory?.tags?.includes("adr"));
112
185
  if (adrs.length > 0) {
113
186
  result += `## ADRs (${adrs.length})\n`;
@@ -117,7 +190,6 @@ export function createSuggestionTools(projectName) {
117
190
  }
118
191
  result += "\n";
119
192
  }
120
- // Graph dependencies
121
193
  const graphResults = graphRes?.data?.results || graphRes?.data?.directResults || [];
122
194
  const connectedFiles = graphRes?.data?.connectedFiles || graphRes?.data?.expandedResults || [];
123
195
  if (graphResults.length > 0 || connectedFiles.length > 0) {
@@ -136,6 +208,26 @@ export function createSuggestionTools(projectName) {
136
208
  return result;
137
209
  },
138
210
  },
211
+ {
212
+ name: "smart_dispatch",
213
+ description: `Intelligent task routing for ${projectName}. LLM analyzes your task and runs only the needed lookups (2-5 of 7 available) in parallel. More efficient than context_briefing for narrow tasks.`,
214
+ schema: z.object({
215
+ task: z.string().describe("What you will implement/change"),
216
+ files: z.array(z.string()).optional().describe("Files you plan to modify"),
217
+ intent: z.enum(["code", "research", "debug", "review", "architecture"]).optional().describe("Task intent for better routing"),
218
+ }),
219
+ annotations: TOOL_ANNOTATIONS["context_briefing"], // Same annotations as context_briefing
220
+ handler: async (args, ctx) => {
221
+ const { task, files, intent } = args;
222
+ const response = await ctx.api.post("/api/smart-dispatch", {
223
+ projectName: ctx.projectName,
224
+ task,
225
+ files,
226
+ intent,
227
+ });
228
+ return formatSmartDispatchResult(task, response.data);
229
+ },
230
+ },
139
231
  {
140
232
  name: "get_contextual_suggestions",
141
233
  description: `Get contextual suggestions based on current work context for ${projectName}. Returns relevant suggestions, triggers, and related memories.`,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crowley/rag-mcp",
3
- "version": "1.0.6",
3
+ "version": "1.1.0",
4
4
  "description": "Universal RAG MCP Server for any project",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",