@echoes-io/mcp-server 4.1.1 → 6.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 (108) hide show
  1. package/README.md +120 -185
  2. package/cli/index.d.ts +1 -0
  3. package/cli/index.d.ts.map +1 -0
  4. package/cli/index.js +3 -185
  5. package/cli/index.js.map +1 -0
  6. package/cli/program.d.ts +3 -0
  7. package/cli/program.d.ts.map +1 -0
  8. package/cli/program.js +150 -0
  9. package/cli/program.js.map +1 -0
  10. package/lib/constants.d.ts +8 -0
  11. package/lib/constants.d.ts.map +1 -0
  12. package/lib/constants.js +12 -0
  13. package/lib/constants.js.map +1 -0
  14. package/lib/database/index.d.ts +34 -0
  15. package/lib/database/index.d.ts.map +1 -0
  16. package/lib/database/index.js +266 -0
  17. package/lib/database/index.js.map +1 -0
  18. package/lib/database/schemas.d.ts +55 -0
  19. package/lib/database/schemas.d.ts.map +1 -0
  20. package/lib/database/schemas.js +70 -0
  21. package/lib/database/schemas.js.map +1 -0
  22. package/lib/indexer/embeddings.d.ts +6 -0
  23. package/lib/indexer/embeddings.d.ts.map +1 -0
  24. package/lib/indexer/embeddings.js +51 -0
  25. package/lib/indexer/embeddings.js.map +1 -0
  26. package/lib/indexer/extractor.d.ts +81 -0
  27. package/lib/indexer/extractor.d.ts.map +1 -0
  28. package/lib/indexer/extractor.js +68 -0
  29. package/lib/indexer/extractor.js.map +1 -0
  30. package/lib/indexer/scanner.d.ts +8 -0
  31. package/lib/indexer/scanner.d.ts.map +1 -0
  32. package/lib/indexer/scanner.js +73 -0
  33. package/lib/indexer/scanner.js.map +1 -0
  34. package/lib/prompts/index.d.ts +13 -0
  35. package/lib/prompts/index.d.ts.map +1 -0
  36. package/lib/prompts/index.js +153 -0
  37. package/lib/prompts/index.js.map +1 -0
  38. package/lib/server.d.ts +13 -0
  39. package/lib/server.d.ts.map +1 -0
  40. package/lib/server.js +90 -0
  41. package/lib/server.js.map +1 -0
  42. package/lib/tools/index.d.ts +19 -0
  43. package/lib/tools/index.d.ts.map +1 -0
  44. package/lib/tools/index.js +128 -0
  45. package/lib/tools/index.js.map +1 -0
  46. package/lib/tools/search.d.ts +86 -0
  47. package/lib/tools/search.d.ts.map +1 -0
  48. package/lib/tools/search.js +95 -0
  49. package/lib/tools/search.js.map +1 -0
  50. package/lib/tools/stats.d.ts +18 -0
  51. package/lib/tools/stats.d.ts.map +1 -0
  52. package/lib/tools/stats.js +62 -0
  53. package/lib/tools/stats.js.map +1 -0
  54. package/{src → lib}/tools/words-count.d.ts +6 -3
  55. package/lib/tools/words-count.d.ts.map +1 -0
  56. package/lib/tools/words-count.js +31 -0
  57. package/lib/tools/words-count.js.map +1 -0
  58. package/{src/types/frontmatter.d.ts → lib/types.d.ts} +11 -17
  59. package/lib/types.d.ts.map +1 -0
  60. package/lib/types.js +2 -0
  61. package/lib/types.js.map +1 -0
  62. package/lib/utils.d.ts +19 -0
  63. package/lib/utils.d.ts.map +1 -0
  64. package/lib/utils.js +40 -0
  65. package/lib/utils.js.map +1 -0
  66. package/package.json +59 -62
  67. package/src/database/index.d.ts +0 -6
  68. package/src/database/index.js +0 -26
  69. package/src/database/relations.d.ts +0 -744
  70. package/src/database/relations.js +0 -52
  71. package/src/database/schema.d.ts +0 -733
  72. package/src/database/schema.js +0 -69
  73. package/src/database/vector.d.ts +0 -25
  74. package/src/database/vector.js +0 -98
  75. package/src/index.d.ts +0 -5
  76. package/src/index.js +0 -5
  77. package/src/rag/character-ner.d.ts +0 -36
  78. package/src/rag/character-ner.js +0 -416
  79. package/src/rag/database-sync.d.ts +0 -38
  80. package/src/rag/database-sync.js +0 -158
  81. package/src/rag/embeddings.d.ts +0 -74
  82. package/src/rag/embeddings.js +0 -164
  83. package/src/rag/graph-rag.d.ts +0 -69
  84. package/src/rag/graph-rag.js +0 -311
  85. package/src/rag/hybrid-rag.d.ts +0 -109
  86. package/src/rag/hybrid-rag.js +0 -255
  87. package/src/rag/index.d.ts +0 -16
  88. package/src/rag/index.js +0 -33
  89. package/src/server.d.ts +0 -43
  90. package/src/server.js +0 -177
  91. package/src/tools/index-rag.d.ts +0 -19
  92. package/src/tools/index-rag.js +0 -85
  93. package/src/tools/index-tracker.d.ts +0 -17
  94. package/src/tools/index-tracker.js +0 -89
  95. package/src/tools/index.d.ts +0 -5
  96. package/src/tools/index.js +0 -5
  97. package/src/tools/rag-context.d.ts +0 -34
  98. package/src/tools/rag-context.js +0 -51
  99. package/src/tools/rag-search.d.ts +0 -35
  100. package/src/tools/rag-search.js +0 -60
  101. package/src/tools/words-count.js +0 -28
  102. package/src/types/frontmatter.js +0 -1
  103. package/src/utils/index.d.ts +0 -1
  104. package/src/utils/index.js +0 -1
  105. package/src/utils/markdown.d.ts +0 -6
  106. package/src/utils/markdown.js +0 -36
  107. package/src/utils/timeline-detection.d.ts +0 -13
  108. package/src/utils/timeline-detection.js +0 -76
@@ -1,158 +0,0 @@
1
- /**
2
- * Database synchronization utilities for HybridRAG
3
- * Ensures timeline/arc/episode/chapter records exist before indexing
4
- */
5
- import { and, eq } from 'drizzle-orm';
6
- import { arcs, chapters, episodes, timelines } from '../database/schema.js';
7
- export class DatabaseSync {
8
- db;
9
- constructor(db) {
10
- this.db = db;
11
- }
12
- /**
13
- * Ensure all required database records exist for chapters
14
- */
15
- async syncChapters(chapterRecords) {
16
- const stats = { timelines: 0, arcs: 0, episodes: 0, chapters: 0 };
17
- // Group by timeline, arc, episode for efficient processing
18
- const timelineMap = new Map();
19
- const arcMap = new Map();
20
- const episodeMap = new Map();
21
- for (const record of chapterRecords) {
22
- // Timeline tracking
23
- if (!timelineMap.has(record.timeline)) {
24
- timelineMap.set(record.timeline, new Set());
25
- }
26
- // Arc tracking
27
- const arcKey = `${record.timeline}:${record.arc}`;
28
- if (!arcMap.has(arcKey)) {
29
- arcMap.set(arcKey, new Set());
30
- timelineMap.get(record.timeline)?.add(record.arc);
31
- }
32
- // Episode tracking
33
- const episodeKey = `${record.timeline}:${record.arc}:${record.episode}`;
34
- if (!episodeMap.has(episodeKey)) {
35
- episodeMap.set(episodeKey, new Set());
36
- arcMap.get(arcKey)?.add(record.episode.toString());
37
- }
38
- }
39
- // 1. Ensure timelines exist
40
- for (const timelineName of timelineMap.keys()) {
41
- const existing = await this.db
42
- .select()
43
- .from(timelines)
44
- .where(eq(timelines.name, timelineName))
45
- .limit(1);
46
- if (existing.length === 0) {
47
- await this.db.insert(timelines).values({
48
- name: timelineName,
49
- description: `Auto-created timeline: ${timelineName}`,
50
- });
51
- stats.timelines++;
52
- }
53
- }
54
- // 2. Ensure arcs exist
55
- for (const [arcKey, _] of arcMap) {
56
- const [timelineName, arcName] = arcKey.split(':');
57
- const timelineRecord = await this.db
58
- .select()
59
- .from(timelines)
60
- .where(eq(timelines.name, timelineName))
61
- .limit(1);
62
- if (timelineRecord.length === 0)
63
- continue;
64
- const existing = await this.db
65
- .select()
66
- .from(arcs)
67
- .where(and(eq(arcs.timelineId, timelineRecord[0].id), eq(arcs.name, arcName)))
68
- .limit(1);
69
- if (existing.length === 0) {
70
- await this.db.insert(arcs).values({
71
- timelineId: timelineRecord[0].id,
72
- name: arcName,
73
- slug: arcName.toLowerCase().replace(/\s+/g, '-'),
74
- order: stats.arcs + 1,
75
- });
76
- stats.arcs++;
77
- }
78
- }
79
- // 3. Ensure episodes exist
80
- for (const [episodeKey, _] of episodeMap) {
81
- const [timelineName, arcName, episodeNum] = episodeKey.split(':');
82
- const arcRecord = await this.db
83
- .select({ id: arcs.id })
84
- .from(arcs)
85
- .innerJoin(timelines, eq(arcs.timelineId, timelines.id))
86
- .where(and(eq(timelines.name, timelineName), eq(arcs.name, arcName)))
87
- .limit(1);
88
- if (arcRecord.length === 0)
89
- continue;
90
- const existing = await this.db
91
- .select()
92
- .from(episodes)
93
- .where(and(eq(episodes.arcId, arcRecord[0].id), eq(episodes.number, parseInt(episodeNum, 10))))
94
- .limit(1);
95
- if (existing.length === 0) {
96
- await this.db.insert(episodes).values({
97
- arcId: arcRecord[0].id,
98
- number: parseInt(episodeNum, 10),
99
- title: `Episode ${episodeNum}`,
100
- slug: `episode-${episodeNum}`,
101
- });
102
- stats.episodes++;
103
- }
104
- }
105
- // 4. Ensure chapters exist
106
- for (const record of chapterRecords) {
107
- const episodeRecord = await this.db
108
- .select({ id: episodes.id })
109
- .from(episodes)
110
- .innerJoin(arcs, eq(episodes.arcId, arcs.id))
111
- .innerJoin(timelines, eq(arcs.timelineId, timelines.id))
112
- .where(and(eq(timelines.name, record.timeline), eq(arcs.name, record.arc), eq(episodes.number, record.episode)))
113
- .limit(1);
114
- if (episodeRecord.length === 0)
115
- continue;
116
- const existing = await this.db
117
- .select()
118
- .from(chapters)
119
- .where(and(eq(chapters.episodeId, episodeRecord[0].id), eq(chapters.number, record.chapter)))
120
- .limit(1);
121
- if (existing.length === 0) {
122
- await this.db.insert(chapters).values({
123
- episodeId: episodeRecord[0].id,
124
- number: record.chapter,
125
- pov: record.pov,
126
- title: record.title || `Chapter ${record.chapter}`,
127
- summary: record.summary || '',
128
- location: record.location || '',
129
- filePath: record.filePath,
130
- });
131
- stats.chapters++;
132
- }
133
- }
134
- return stats;
135
- }
136
- /**
137
- * Get chapter ID from database for embedding insertion
138
- */
139
- async getChapterId(timeline, arc, episode, chapter) {
140
- const result = await this.db
141
- .select({ id: chapters.id })
142
- .from(chapters)
143
- .innerJoin(episodes, eq(chapters.episodeId, episodes.id))
144
- .innerJoin(arcs, eq(episodes.arcId, arcs.id))
145
- .innerJoin(timelines, eq(arcs.timelineId, timelines.id))
146
- .where(and(eq(timelines.name, timeline), eq(arcs.name, arc), eq(episodes.number, episode), eq(chapters.number, chapter)))
147
- .limit(1);
148
- return result.length > 0 ? result[0].id : null;
149
- }
150
- /**
151
- * Clean up orphaned embeddings
152
- */
153
- async cleanupEmbeddings() {
154
- // This would require a more complex query to find orphaned embeddings
155
- // For now, we'll implement a simple version
156
- return 0;
157
- }
158
- }
@@ -1,74 +0,0 @@
1
- /**
2
- * Embedding providers for GraphRAG
3
- * Supports BGE-Base-v1.5, E5-Small-v2, and future models
4
- */
5
- export interface EmbeddingProvider {
6
- name: string;
7
- dimension: number;
8
- embed(texts: string[]): Promise<number[][]>;
9
- embedSingle(text: string): Promise<number[]>;
10
- }
11
- export interface EmbeddingConfig {
12
- provider: 'bge-base' | 'e5-small' | 'gemini';
13
- batchSize?: number;
14
- maxTokens?: number;
15
- }
16
- /**
17
- * BGE-Base-v1.5 Embedding Provider
18
- * Fast, accurate, 384-dimensional embeddings
19
- */
20
- export declare class BGEBaseEmbedding implements EmbeddingProvider {
21
- name: string;
22
- dimension: number;
23
- private batchSize;
24
- private maxTokens;
25
- constructor(config?: {
26
- batchSize?: number;
27
- maxTokens?: number;
28
- });
29
- embed(texts: string[]): Promise<number[][]>;
30
- embedSingle(text: string): Promise<number[]>;
31
- private generateContentAwareEmbedding;
32
- }
33
- /**
34
- * E5-Small-v2 Embedding Provider
35
- * Lightweight, fast, 384-dimensional embeddings for testing
36
- */
37
- export declare class E5SmallEmbedding implements EmbeddingProvider {
38
- name: string;
39
- dimension: number;
40
- private batchSize;
41
- constructor(config?: {
42
- batchSize?: number;
43
- });
44
- embed(texts: string[]): Promise<number[][]>;
45
- embedSingle(text: string): Promise<number[]>;
46
- private generateContentAwareEmbedding;
47
- }
48
- /**
49
- * Gemini Embedding Provider
50
- * State-of-the-art, requires API key
51
- */
52
- export declare class GeminiEmbedding implements EmbeddingProvider {
53
- name: string;
54
- dimension: number;
55
- constructor(apiKey: string);
56
- embed(_texts: string[]): Promise<number[][]>;
57
- embedSingle(text: string): Promise<number[]>;
58
- }
59
- /**
60
- * Factory function to create embedding providers
61
- */
62
- export declare function createEmbeddingProvider(config: EmbeddingConfig): EmbeddingProvider;
63
- /**
64
- * Calculate cosine similarity between two vectors
65
- */
66
- export declare function cosineSimilarity(a: number[], b: number[]): number;
67
- /**
68
- * Normalize embedding vector to unit length
69
- */
70
- export declare function normalizeEmbedding(embedding: number[]): number[];
71
- /**
72
- * Split array into batches of specified size
73
- */
74
- export declare function batchArray<T>(array: T[], batchSize: number): T[][];
@@ -1,164 +0,0 @@
1
- /**
2
- * Embedding providers for GraphRAG
3
- * Supports BGE-Base-v1.5, E5-Small-v2, and future models
4
- */
5
- /**
6
- * BGE-Base-v1.5 Embedding Provider
7
- * Fast, accurate, 384-dimensional embeddings
8
- */
9
- export class BGEBaseEmbedding {
10
- name = 'bge-base-en-v1.5';
11
- dimension = 384;
12
- batchSize;
13
- maxTokens;
14
- constructor(config = {}) {
15
- this.batchSize = config.batchSize || 32;
16
- this.maxTokens = config.maxTokens || 512;
17
- }
18
- async embed(texts) {
19
- // Create content-aware mock embeddings for more realistic similarity
20
- return texts.map((text, index) => this.generateContentAwareEmbedding(text, index));
21
- }
22
- async embedSingle(text) {
23
- return this.generateContentAwareEmbedding(text, 0);
24
- }
25
- generateContentAwareEmbedding(text, index) {
26
- // Create embeddings based on text content for more realistic similarity
27
- const words = text.toLowerCase().match(/\b\w+\b/g) || [];
28
- const wordSet = new Set(words);
29
- // Use text characteristics to create different embeddings
30
- const textLength = text.length;
31
- const uniqueWords = wordSet.size;
32
- const avgWordLength = words.reduce((sum, word) => sum + word.length, 0) / words.length || 1;
33
- // Create embedding with some structure based on content
34
- const embedding = Array.from({ length: this.dimension }, (_, i) => {
35
- // Base pattern from text characteristics
36
- const base = Math.sin((textLength + i) * 0.01) * 0.2;
37
- // Add word-based variation
38
- const wordInfluence = Math.cos((uniqueWords + i) * 0.02) * 0.2;
39
- // Add length-based variation
40
- const lengthInfluence = Math.sin((avgWordLength + i) * 0.03) * 0.2;
41
- // Add index-based variation to ensure uniqueness
42
- const indexInfluence = Math.cos((index + i) * 0.05) * 0.3;
43
- // Random noise (reduced)
44
- const noise = (Math.random() - 0.5) * 0.1;
45
- return base + wordInfluence + lengthInfluence + indexInfluence + noise;
46
- });
47
- // Normalize to unit vector
48
- const norm = Math.sqrt(embedding.reduce((sum, val) => sum + val * val, 0));
49
- return embedding.map((val) => val / norm);
50
- }
51
- }
52
- /**
53
- * E5-Small-v2 Embedding Provider
54
- * Lightweight, fast, 384-dimensional embeddings for testing
55
- */
56
- export class E5SmallEmbedding {
57
- name = 'e5-small-v2';
58
- dimension = 384;
59
- batchSize;
60
- constructor(config = {}) {
61
- this.batchSize = config.batchSize || 64;
62
- }
63
- async embed(texts) {
64
- return texts.map((text, index) => this.generateContentAwareEmbedding(text, index));
65
- }
66
- async embedSingle(text) {
67
- return this.generateContentAwareEmbedding(text, 0);
68
- }
69
- generateContentAwareEmbedding(text, index) {
70
- const words = text.toLowerCase().match(/\b\w+\b/g) || [];
71
- const wordSet = new Set(words);
72
- const textLength = text.length;
73
- const uniqueWords = wordSet.size;
74
- const avgWordLength = words.reduce((sum, word) => sum + word.length, 0) / words.length || 1;
75
- const embedding = Array.from({ length: this.dimension }, (_, i) => {
76
- const base = Math.sin((textLength + i) * 0.01) * 0.2;
77
- const wordInfluence = Math.cos((uniqueWords + i) * 0.02) * 0.2;
78
- const lengthInfluence = Math.sin((avgWordLength + i) * 0.03) * 0.2;
79
- const indexInfluence = Math.cos((index + i) * 0.05) * 0.3;
80
- const noise = (Math.random() - 0.5) * 0.1;
81
- return base + wordInfluence + lengthInfluence + indexInfluence + noise;
82
- });
83
- const norm = Math.sqrt(embedding.reduce((sum, val) => sum + val * val, 0));
84
- return embedding.map((val) => val / norm);
85
- }
86
- }
87
- /**
88
- * Gemini Embedding Provider
89
- * State-of-the-art, requires API key
90
- */
91
- export class GeminiEmbedding {
92
- name = 'gemini-embedding';
93
- dimension = 768; // Gemini embedding dimension
94
- constructor(apiKey) {
95
- // Store API key for future implementation
96
- void apiKey;
97
- }
98
- async embed(_texts) {
99
- // TODO: Implement Gemini API integration
100
- throw new Error('Gemini embedding not yet implemented');
101
- }
102
- async embedSingle(text) {
103
- const results = await this.embed([text]);
104
- return results[0];
105
- }
106
- }
107
- /**
108
- * Factory function to create embedding providers
109
- */
110
- export function createEmbeddingProvider(config) {
111
- switch (config.provider) {
112
- case 'bge-base':
113
- return new BGEBaseEmbedding({
114
- batchSize: config.batchSize,
115
- maxTokens: config.maxTokens,
116
- });
117
- case 'e5-small':
118
- return new E5SmallEmbedding({
119
- batchSize: config.batchSize,
120
- });
121
- case 'gemini': {
122
- const apiKey = process.env.ECHOES_GEMINI_API_KEY;
123
- if (!apiKey) {
124
- throw new Error('ECHOES_GEMINI_API_KEY environment variable required for Gemini provider');
125
- }
126
- return new GeminiEmbedding(apiKey);
127
- }
128
- default:
129
- throw new Error(`Unknown embedding provider: ${config.provider}`);
130
- }
131
- }
132
- /**
133
- * Calculate cosine similarity between two vectors
134
- */
135
- export function cosineSimilarity(a, b) {
136
- if (a.length !== b.length)
137
- return 0;
138
- let dotProduct = 0;
139
- let normA = 0;
140
- let normB = 0;
141
- for (let i = 0; i < a.length; i++) {
142
- dotProduct += a[i] * b[i];
143
- normA += a[i] * a[i];
144
- normB += b[i] * b[i];
145
- }
146
- return dotProduct / (Math.sqrt(normA) * Math.sqrt(normB));
147
- }
148
- /**
149
- * Normalize embedding vector to unit length
150
- */
151
- export function normalizeEmbedding(embedding) {
152
- const norm = Math.sqrt(embedding.reduce((sum, val) => sum + val * val, 0));
153
- return embedding.map((val) => val / norm);
154
- }
155
- /**
156
- * Split array into batches of specified size
157
- */
158
- export function batchArray(array, batchSize) {
159
- const batches = [];
160
- for (let i = 0; i < array.length; i += batchSize) {
161
- batches.push(array.slice(i, i + batchSize));
162
- }
163
- return batches;
164
- }
@@ -1,69 +0,0 @@
1
- /**
2
- * GraphRAG implementation for Echoes storytelling platform
3
- * Based on Mastra.ai GraphRAG with adaptations for chapter relationships
4
- */
5
- export type SupportedEdgeType = 'semantic' | 'character' | 'temporal' | 'location';
6
- export interface GraphNode {
7
- id: string;
8
- content: string;
9
- embedding?: number[];
10
- metadata: {
11
- chapterId: string;
12
- arc: string;
13
- episode: number;
14
- chapter: number;
15
- pov: string;
16
- characters: string[];
17
- location?: string;
18
- [key: string]: unknown;
19
- };
20
- }
21
- export interface RankedNode extends GraphNode {
22
- score: number;
23
- }
24
- export interface GraphEdge {
25
- source: string;
26
- target: string;
27
- weight: number;
28
- type: SupportedEdgeType;
29
- }
30
- export interface GraphChunk {
31
- text: string;
32
- metadata: GraphNode['metadata'];
33
- }
34
- export interface GraphEmbedding {
35
- vector: number[];
36
- }
37
- export declare class GraphRAG {
38
- private nodes;
39
- private edges;
40
- private dimension;
41
- private threshold;
42
- constructor(dimension?: number, threshold?: number);
43
- addNode(node: GraphNode): void;
44
- addEdge(edge: GraphEdge): void;
45
- getNodes(): GraphNode[];
46
- getEdges(): GraphEdge[];
47
- getEdgesByType(type: SupportedEdgeType): GraphEdge[];
48
- clear(): void;
49
- updateNodeContent(id: string, newContent: string): void;
50
- private getNeighbors;
51
- private cosineSimilarity;
52
- createGraph(chunks: GraphChunk[], embeddings: GraphEmbedding[]): void;
53
- private createSemanticEdges;
54
- private createCharacterEdges;
55
- private createTemporalEdges;
56
- private createLocationEdges;
57
- private selectWeightedNeighbor;
58
- private randomWalkWithRestart;
59
- query({ query, topK, randomWalkSteps, restartProb, characters, allCharacters, arc, pov, }: {
60
- query: number[];
61
- topK?: number;
62
- randomWalkSteps?: number;
63
- restartProb?: number;
64
- characters?: string[];
65
- allCharacters?: boolean;
66
- arc?: string;
67
- pov?: string;
68
- }): RankedNode[];
69
- }