knowledge-mcp-server 1.0.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.
Files changed (52) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/LICENSE +21 -0
  3. package/README.md +205 -0
  4. package/dist/analytics.d.ts +24 -0
  5. package/dist/analytics.js +102 -0
  6. package/dist/analytics.js.map +1 -0
  7. package/dist/cli.d.ts +2 -0
  8. package/dist/cli.js +98 -0
  9. package/dist/cli.js.map +1 -0
  10. package/dist/config.d.ts +39 -0
  11. package/dist/config.js +80 -0
  12. package/dist/config.js.map +1 -0
  13. package/dist/embeddings.d.ts +26 -0
  14. package/dist/embeddings.js +534 -0
  15. package/dist/embeddings.js.map +1 -0
  16. package/dist/formatter.d.ts +51 -0
  17. package/dist/formatter.js +273 -0
  18. package/dist/formatter.js.map +1 -0
  19. package/dist/generate-embeddings.d.ts +8 -0
  20. package/dist/generate-embeddings.js +146 -0
  21. package/dist/generate-embeddings.js.map +1 -0
  22. package/dist/graph.d.ts +20 -0
  23. package/dist/graph.js +133 -0
  24. package/dist/graph.js.map +1 -0
  25. package/dist/index.d.ts +14 -0
  26. package/dist/index.js +481 -0
  27. package/dist/index.js.map +1 -0
  28. package/dist/init.d.ts +1 -0
  29. package/dist/init.js +65 -0
  30. package/dist/init.js.map +1 -0
  31. package/dist/loader.d.ts +26 -0
  32. package/dist/loader.js +151 -0
  33. package/dist/loader.js.map +1 -0
  34. package/dist/logger.d.ts +6 -0
  35. package/dist/logger.js +15 -0
  36. package/dist/logger.js.map +1 -0
  37. package/dist/query-classifier.d.ts +23 -0
  38. package/dist/query-classifier.js +111 -0
  39. package/dist/query-classifier.js.map +1 -0
  40. package/dist/reranker.d.ts +7 -0
  41. package/dist/reranker.js +38 -0
  42. package/dist/reranker.js.map +1 -0
  43. package/dist/search.d.ts +17 -0
  44. package/dist/search.js +299 -0
  45. package/dist/search.js.map +1 -0
  46. package/dist/validator.d.ts +23 -0
  47. package/dist/validator.js +97 -0
  48. package/dist/validator.js.map +1 -0
  49. package/dist/writer.d.ts +36 -0
  50. package/dist/writer.js +360 -0
  51. package/dist/writer.js.map +1 -0
  52. package/package.json +58 -0
package/dist/search.js ADDED
@@ -0,0 +1,299 @@
1
+ import { getAncestors, getRelated } from "./graph.js";
2
+ import { classifyQuery, expandSynonyms } from "./query-classifier.js";
3
+ import { cosineSimilarity, embedQuery, bm25Score, tokenize } from "./embeddings.js";
4
+ import { formatSearchResults, } from "./formatter.js";
5
+ import { rerank } from "./reranker.js";
6
+ import { log } from "./logger.js";
7
+ // Stage 1: Query classification (rule-based)
8
+ function classifyAndMerge(options, classifierConfig) {
9
+ const classification = classifyQuery(options.query, classifierConfig);
10
+ // Merge explicit filters with classified ones
11
+ const domains = options.domains?.length ? options.domains : classification.domains;
12
+ const phases = options.phases?.length ? options.phases : classification.phases;
13
+ return { domains, phases, queryType: classification.queryType };
14
+ }
15
+ // Stage 2: Metadata pre-filter (uses phaseIndex for O(1) phase lookups)
16
+ function metadataFilter(graph, domains, phases, tags, type) {
17
+ let candidates = null;
18
+ // Domain filter
19
+ if (domains.length > 0) {
20
+ candidates = new Set();
21
+ for (const domain of domains) {
22
+ const domainDocs = graph.domainIndex.get(domain.toLowerCase());
23
+ if (domainDocs) {
24
+ for (const id of domainDocs)
25
+ candidates.add(id);
26
+ }
27
+ }
28
+ }
29
+ // Phase filter (using phaseIndex instead of iterating all documents)
30
+ if (phases.length > 0) {
31
+ const phaseFiltered = new Set();
32
+ for (const phase of phases) {
33
+ const phaseDocs = graph.phaseIndex.get(phase);
34
+ if (phaseDocs) {
35
+ for (const id of phaseDocs)
36
+ phaseFiltered.add(id);
37
+ }
38
+ }
39
+ if (candidates) {
40
+ candidates = new Set([...candidates].filter((id) => phaseFiltered.has(id)));
41
+ }
42
+ else {
43
+ candidates = phaseFiltered;
44
+ }
45
+ }
46
+ // Tag filter
47
+ if (tags && tags.length > 0) {
48
+ const tagFiltered = new Set();
49
+ for (const tag of tags) {
50
+ const tagDocs = graph.tagIndex.get(tag.toLowerCase());
51
+ if (tagDocs) {
52
+ for (const id of tagDocs)
53
+ tagFiltered.add(id);
54
+ }
55
+ }
56
+ if (candidates) {
57
+ candidates = new Set([...candidates].filter((id) => tagFiltered.has(id)));
58
+ }
59
+ else {
60
+ candidates = tagFiltered;
61
+ }
62
+ }
63
+ // Type filter
64
+ if (type) {
65
+ const typeDocs = graph.typeIndex.get(type);
66
+ const typeFiltered = typeDocs ? new Set(typeDocs) : new Set();
67
+ if (candidates) {
68
+ candidates = new Set([...candidates].filter((id) => typeFiltered.has(id)));
69
+ }
70
+ else {
71
+ candidates = typeFiltered;
72
+ }
73
+ }
74
+ // No filters → all documents
75
+ if (!candidates) {
76
+ candidates = new Set(graph.documents.keys());
77
+ }
78
+ return candidates;
79
+ }
80
+ // Determine which fields a query matched against
81
+ function detectMatchedOn(query, doc) {
82
+ const queryTokens = new Set(tokenize(query));
83
+ const parts = [];
84
+ const titleTokens = tokenize(doc.title);
85
+ if (titleTokens.some((t) => queryTokens.has(t)))
86
+ parts.push("title");
87
+ const tagTokens = doc.tags.flatMap((t) => tokenize(t));
88
+ if (tagTokens.some((t) => queryTokens.has(t)))
89
+ parts.push("tags");
90
+ const bodyTokens = tokenize(doc.contentBody.slice(0, 2000));
91
+ if (bodyTokens.some((t) => queryTokens.has(t)))
92
+ parts.push("body");
93
+ return parts.length > 0 ? parts.join("+") : "semantic";
94
+ }
95
+ // Reciprocal Rank Fusion constant
96
+ const RRF_K = 60;
97
+ // Stage 3: Hybrid scoring — vector + BM25 merged via RRF when embeddings available
98
+ async function scoreDocuments(graph, candidates, query, bm25Index, synonymMap) {
99
+ let queryVector = null;
100
+ // Expand synonyms for BM25 (vector embeddings handle semantic similarity natively)
101
+ const expandedQuery = expandSynonyms(query, synonymMap);
102
+ // Always compute BM25 scores (using synonym-expanded query)
103
+ const bm25Scored = [];
104
+ for (const id of candidates) {
105
+ const doc = graph.documents.get(id);
106
+ if (!doc)
107
+ continue;
108
+ const score = bm25Score(expandedQuery, id, bm25Index);
109
+ bm25Scored.push({ doc, score });
110
+ }
111
+ bm25Scored.sort((a, b) => b.score - a.score);
112
+ // Try vector similarity
113
+ if (graph.embeddings.available) {
114
+ queryVector = await embedQuery(query);
115
+ }
116
+ if (queryVector) {
117
+ // Hybrid: compute vector scores, merge with RRF
118
+ const vectorScored = [];
119
+ for (const id of candidates) {
120
+ const docVector = graph.embeddings.vectors.get(id);
121
+ const doc = graph.documents.get(id);
122
+ if (!doc)
123
+ continue;
124
+ const score = docVector ? cosineSimilarity(queryVector, docVector) : 0;
125
+ vectorScored.push({ doc, score });
126
+ }
127
+ vectorScored.sort((a, b) => b.score - a.score);
128
+ // Build rank maps
129
+ const vectorRank = new Map();
130
+ const bm25Rank = new Map();
131
+ for (let i = 0; i < vectorScored.length; i++) {
132
+ vectorRank.set(vectorScored[i].doc.id, i + 1);
133
+ }
134
+ for (let i = 0; i < bm25Scored.length; i++) {
135
+ bm25Rank.set(bm25Scored[i].doc.id, i + 1);
136
+ }
137
+ // Merge via RRF
138
+ const merged = [];
139
+ const allIds = new Set([...vectorRank.keys(), ...bm25Rank.keys()]);
140
+ for (const id of allIds) {
141
+ const doc = graph.documents.get(id);
142
+ if (!doc)
143
+ continue;
144
+ const vr = vectorRank.get(id) ?? vectorScored.length + 1;
145
+ const br = bm25Rank.get(id) ?? bm25Scored.length + 1;
146
+ const score = 1 / (RRF_K + vr) + 1 / (RRF_K + br);
147
+ merged.push({ doc, score });
148
+ }
149
+ merged.sort((a, b) => b.score - a.score);
150
+ return { scored: merged, queryVector, searchMethod: "vector+bm25" };
151
+ }
152
+ // BM25-only fallback
153
+ return { scored: bm25Scored, queryVector, searchMethod: "bm25_fallback" };
154
+ }
155
+ // Stage 4: Hierarchical expansion + graph walk
156
+ function expandResults(graph, topResults, maxTotal, bm25Index, queryVector, query, maxRelated, searchMethod) {
157
+ const seen = new Set();
158
+ const formattedDocs = [];
159
+ // 4a: Collect unique ancestors (never dropped)
160
+ const ancestorDocs = [];
161
+ for (const { doc } of topResults) {
162
+ const ancestors = getAncestors(graph, doc.id);
163
+ for (const a of ancestors) {
164
+ if (!seen.has(a.id)) {
165
+ seen.add(a.id);
166
+ ancestorDocs.push(a);
167
+ }
168
+ }
169
+ }
170
+ for (const a of ancestorDocs) {
171
+ formattedDocs.push({ doc: a, relevance: "ancestor" });
172
+ }
173
+ // Primary results (with annotations)
174
+ for (const { doc, score } of topResults) {
175
+ if (!seen.has(doc.id)) {
176
+ seen.add(doc.id);
177
+ formattedDocs.push({
178
+ doc,
179
+ relevance: "primary",
180
+ similarity: score,
181
+ matchedOn: detectMatchedOn(query, doc),
182
+ scoringMethod: searchMethod,
183
+ });
184
+ }
185
+ }
186
+ // 4b: Related-link expansion (skip if maxRelated is 0)
187
+ if (maxRelated <= 0)
188
+ return formattedDocs;
189
+ // Compute median primary score for BM25 threshold
190
+ const primaryScores = topResults.map((r) => r.score).sort((a, b) => a - b);
191
+ const medianPrimaryScore = primaryScores.length > 0 ? primaryScores[Math.floor(primaryScores.length / 2)] : 0;
192
+ const relatedCandidates = [];
193
+ const topForExpansion = topResults.slice(0, 4);
194
+ for (const { doc } of topForExpansion) {
195
+ const related = getRelated(graph, doc.id);
196
+ for (const r of related) {
197
+ if (!seen.has(r.id)) {
198
+ // Score the related doc — use embeddings if available, BM25 fallback
199
+ let score;
200
+ let useVector = false;
201
+ const docVector = queryVector ? graph.embeddings.vectors.get(r.id) : undefined;
202
+ if (queryVector && docVector) {
203
+ score = cosineSimilarity(queryVector, docVector);
204
+ useVector = true;
205
+ }
206
+ else {
207
+ score = bm25Score(query, r.id, bm25Index);
208
+ }
209
+ // Score threshold: filter out low-relevance related docs
210
+ const minScore = useVector ? 0.3 : 0.4 * medianPrimaryScore;
211
+ if (score >= minScore) {
212
+ relatedCandidates.push({ doc: r, score, sourceId: doc.id });
213
+ }
214
+ }
215
+ }
216
+ }
217
+ // Sort related by score, take up to budget
218
+ relatedCandidates.sort((a, b) => b.score - a.score);
219
+ const budget = maxTotal - formattedDocs.length;
220
+ const relatedToAdd = relatedCandidates.slice(0, Math.min(maxRelated, budget));
221
+ for (const { doc, score, sourceId } of relatedToAdd) {
222
+ if (!seen.has(doc.id)) {
223
+ seen.add(doc.id);
224
+ formattedDocs.push({
225
+ doc,
226
+ relevance: "graph-expanded",
227
+ similarity: score > 0 ? score : undefined,
228
+ expandedFrom: sourceId,
229
+ scoringMethod: searchMethod,
230
+ });
231
+ }
232
+ }
233
+ return formattedDocs;
234
+ }
235
+ export async function knowledgeSearch(graph, bm25Index, options, classifierConfig) {
236
+ const maxResults = options.maxResults || 10;
237
+ const detailLevel = options.detailLevel || "normal";
238
+ const searchStart = Date.now();
239
+ // Stage 1: Classify query
240
+ const { domains, phases, queryType } = classifyAndMerge(options, classifierConfig);
241
+ // Stage 2: Metadata pre-filter
242
+ const candidates = metadataFilter(graph, domains, phases, options.tags, options.type);
243
+ // Stage 3: Score documents
244
+ const { scored: rawScored, queryVector, searchMethod, } = await scoreDocuments(graph, candidates, options.query, bm25Index, classifierConfig.synonymMap);
245
+ // Re-rank top candidates (title match, decision boost, staleness, exact phrase)
246
+ const reranked = rerank(rawScored.slice(0, 20), options.query, queryType);
247
+ const remaining = rawScored.slice(20);
248
+ // Filter drafts (unless explicitly included), penalize deprecated (0.5x score)
249
+ const scored = [...reranked, ...remaining]
250
+ .filter((s) => options.includeDrafts || s.doc.status !== "draft")
251
+ .map((s) => (s.doc.status === "deprecated" ? { ...s, score: s.score * 0.5 } : s));
252
+ scored.sort((a, b) => b.score - a.score);
253
+ // Adaptive top-N based on query type
254
+ let topN;
255
+ let maxRelated;
256
+ switch (queryType) {
257
+ case "broad":
258
+ topN = Math.min(4, scored.length);
259
+ maxRelated = 2;
260
+ break;
261
+ case "decision":
262
+ topN = Math.min(3, scored.length);
263
+ maxRelated = 0;
264
+ break;
265
+ case "specific":
266
+ default:
267
+ topN = Math.min(6, scored.length);
268
+ maxRelated = 3;
269
+ break;
270
+ }
271
+ const topResults = scored.slice(0, topN);
272
+ // Stage 4: Expand with ancestors + related
273
+ const formatted = expandResults(graph, topResults, maxResults, bm25Index, queryVector, options.query, maxRelated, searchMethod);
274
+ // Compute facets from all scored candidates (not just top-N)
275
+ const facets = {
276
+ domains: new Map(),
277
+ types: new Map(),
278
+ phases: new Map(),
279
+ };
280
+ for (const { doc } of scored) {
281
+ facets.domains.set(doc.domain, (facets.domains.get(doc.domain) || 0) + 1);
282
+ facets.types.set(doc.type, (facets.types.get(doc.type) || 0) + 1);
283
+ for (const p of doc.phase) {
284
+ const key = `phase-${p}`;
285
+ facets.phases.set(key, (facets.phases.get(key) || 0) + 1);
286
+ }
287
+ }
288
+ log.info("search", {
289
+ query: options.query,
290
+ queryType,
291
+ method: searchMethod,
292
+ candidates: candidates.size,
293
+ results: formatted.length,
294
+ ms: Date.now() - searchStart,
295
+ });
296
+ // Stage 5: Format for Claude
297
+ return formatSearchResults(options.query, formatted, detailLevel, searchMethod, facets);
298
+ }
299
+ //# sourceMappingURL=search.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search.js","sourceRoot":"","sources":["../src/search.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ import type { KnowledgeGraph } from "./graph.js";
2
+ export interface ValidationReport {
3
+ orphans: string[];
4
+ brokenRelated: Array<{
5
+ doc: string;
6
+ ref: string;
7
+ }>;
8
+ brokenChildren: Array<{
9
+ doc: string;
10
+ ref: string;
11
+ }>;
12
+ circularParents: string[];
13
+ noTags: string[];
14
+ emptySummaries: string[];
15
+ staleDocs: string[];
16
+ embeddingCoverage: {
17
+ total: number;
18
+ covered: number;
19
+ percent: number;
20
+ };
21
+ }
22
+ export declare function validateGraph(graph: KnowledgeGraph): ValidationReport;
23
+ export declare function formatValidationReport(report: ValidationReport): string;
@@ -0,0 +1,97 @@
1
+ const SIX_MONTHS_MS = 180 * 24 * 60 * 60 * 1000;
2
+ export function validateGraph(graph) {
3
+ const now = Date.now();
4
+ const report = {
5
+ orphans: [],
6
+ brokenRelated: [],
7
+ brokenChildren: [],
8
+ circularParents: [],
9
+ noTags: [],
10
+ emptySummaries: [],
11
+ staleDocs: [],
12
+ embeddingCoverage: { total: 0, covered: 0, percent: 0 },
13
+ };
14
+ for (const doc of graph.documents.values()) {
15
+ // Orphan check: has a parentId that doesn't exist (and isn't a top-level domain doc)
16
+ if (doc.parentId && doc.parentId !== "root" && !graph.documents.has(doc.parentId)) {
17
+ report.orphans.push(doc.id);
18
+ }
19
+ // Broken related references
20
+ for (const ref of doc.related) {
21
+ if (!graph.documents.has(ref)) {
22
+ report.brokenRelated.push({ doc: doc.id, ref });
23
+ }
24
+ }
25
+ // Broken children references
26
+ for (const ref of doc.childrenIds) {
27
+ if (!graph.documents.has(ref)) {
28
+ report.brokenChildren.push({ doc: doc.id, ref });
29
+ }
30
+ }
31
+ // Circular parent chain detection
32
+ const visited = new Set();
33
+ let current = doc.id;
34
+ let circular = false;
35
+ while (current) {
36
+ if (visited.has(current)) {
37
+ circular = true;
38
+ break;
39
+ }
40
+ visited.add(current);
41
+ current = graph.documents.get(current)?.parentId ?? null;
42
+ }
43
+ if (circular) {
44
+ report.circularParents.push(doc.id);
45
+ }
46
+ // Docs with zero tags
47
+ if (doc.tags.length === 0) {
48
+ report.noTags.push(doc.id);
49
+ }
50
+ // Summary nodes without children
51
+ if (doc.type === "summary" && doc.childrenIds.length === 0) {
52
+ report.emptySummaries.push(doc.id);
53
+ }
54
+ // Stale docs (>6 months since last update)
55
+ if (doc.lastUpdated) {
56
+ const docDate = new Date(doc.lastUpdated).getTime();
57
+ if (!isNaN(docDate) && now - docDate > SIX_MONTHS_MS) {
58
+ report.staleDocs.push(doc.id);
59
+ }
60
+ }
61
+ }
62
+ // Embedding coverage
63
+ const total = graph.documents.size;
64
+ const covered = [...graph.documents.keys()].filter((id) => graph.embeddings.vectors.has(id)).length;
65
+ report.embeddingCoverage = {
66
+ total,
67
+ covered,
68
+ percent: total > 0 ? Math.round((covered / total) * 100) : 0,
69
+ };
70
+ return report;
71
+ }
72
+ export function formatValidationReport(report) {
73
+ const lines = ["Knowledge Graph Validation Report", "=".repeat(38), ""];
74
+ const addSection = (title, items) => {
75
+ lines.push(`${title}: ${items.length === 0 ? "none" : items.length}`);
76
+ for (const item of items) {
77
+ lines.push(` - ${item}`);
78
+ }
79
+ };
80
+ addSection("Orphaned documents", report.orphans);
81
+ addSection("Broken related references", report.brokenRelated.map((r) => `${r.doc} → ${r.ref}`));
82
+ addSection("Broken children references", report.brokenChildren.map((r) => `${r.doc} → ${r.ref}`));
83
+ addSection("Circular parent chains", report.circularParents);
84
+ addSection("Documents with no tags", report.noTags);
85
+ addSection("Empty summary nodes (no children)", report.emptySummaries);
86
+ addSection("Stale documents (>6 months)", report.staleDocs);
87
+ lines.push("");
88
+ lines.push(`Embedding coverage: ${report.embeddingCoverage.covered}/${report.embeddingCoverage.total} (${report.embeddingCoverage.percent}%)`);
89
+ const issueCount = report.orphans.length +
90
+ report.brokenRelated.length +
91
+ report.brokenChildren.length +
92
+ report.circularParents.length;
93
+ lines.push("");
94
+ lines.push(issueCount === 0 ? "No integrity issues found." : `${issueCount} integrity issue(s) found.`);
95
+ return lines.join("\n");
96
+ }
97
+ //# sourceMappingURL=validator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validator.js","sourceRoot":"","sources":["../src/validator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ import { type DocumentStatus } from "./loader.js";
2
+ import { type Bm25Index } from "./embeddings.js";
3
+ import type { KnowledgeGraph } from "./graph.js";
4
+ export type TfIdfIndex = Bm25Index;
5
+ export interface WriteParams {
6
+ id: string;
7
+ title: string;
8
+ type: string;
9
+ domain: string;
10
+ subdomain?: string;
11
+ tags: string[];
12
+ phase: number[];
13
+ related?: string[];
14
+ children?: string[];
15
+ content: string;
16
+ status?: DocumentStatus;
17
+ superseded_by?: string;
18
+ decision_status?: "proposed" | "accepted" | "deprecated" | "superseded" | "finalized";
19
+ alternatives_considered?: string[];
20
+ decision_date?: string;
21
+ }
22
+ export interface WriteResult {
23
+ id: string;
24
+ filePath: string;
25
+ parentId: string | null;
26
+ status: "created" | "updated";
27
+ tfidfIndex: Bm25Index;
28
+ warnings: string[];
29
+ }
30
+ export interface DeleteResult {
31
+ id: string;
32
+ warnings: string[];
33
+ tfidfIndex: Bm25Index;
34
+ }
35
+ export declare function writeDocument(graph: KnowledgeGraph, tfidfIndex: Bm25Index, knowledgeDir: string, params: WriteParams, validDomains?: string[] | null, validPhaseIds?: number[] | null): WriteResult;
36
+ export declare function deleteDocument(graph: KnowledgeGraph, tfidfIndex: Bm25Index, knowledgeDir: string, id: string): DeleteResult;