@echoes-io/mcp-server 1.3.1 → 1.3.3

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/README.md CHANGED
@@ -97,8 +97,9 @@ All tools operate on the timeline specified by the `ECHOES_TIMELINE` environment
97
97
 
98
98
  ### RAG (Semantic Search)
99
99
  - **`rag-index`** - Index chapters into vector database for semantic search
100
- - Input: optional: `arc`, `episode` (to index specific content)
100
+ - Input: `contentPath` (path to content directory, required for full content indexing), optional: `arc`, `episode` (to index specific content)
101
101
  - Output: Number of chapters indexed
102
+ - Note: Requires `contentPath` to read and index actual chapter content. Without it, only metadata is indexed.
102
103
 
103
104
  - **`rag-search`** - Semantic search across timeline content
104
105
  - Input: `query`, optional: `arc`, `pov`, `maxResults`
@@ -133,7 +134,7 @@ npm run build
133
134
  npm run lint
134
135
 
135
136
  # Fix linting issues
136
- npm run lint:fix
137
+ npm run lint:format
137
138
  ```
138
139
 
139
140
  ### Tech Stack
@@ -2,12 +2,15 @@ import type { RAGSystem } from '@echoes-io/rag';
2
2
  import type { Tracker } from '@echoes-io/tracker';
3
3
  import { z } from 'zod';
4
4
  export declare const ragIndexSchema: z.ZodObject<{
5
+ contentPath: z.ZodOptional<z.ZodString>;
5
6
  arc: z.ZodOptional<z.ZodString>;
6
7
  episode: z.ZodOptional<z.ZodNumber>;
7
8
  }, "strip", z.ZodTypeAny, {
9
+ contentPath?: string | undefined;
8
10
  arc?: string | undefined;
9
11
  episode?: number | undefined;
10
12
  }, {
13
+ contentPath?: string | undefined;
11
14
  arc?: string | undefined;
12
15
  episode?: number | undefined;
13
16
  }>;
@@ -1,6 +1,10 @@
1
+ import { readdirSync, readFileSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { parseMarkdown } from '@echoes-io/utils';
1
4
  import { z } from 'zod';
2
5
  import { getTimeline } from '../utils.js';
3
6
  export const ragIndexSchema = z.object({
7
+ contentPath: z.string().optional().describe('Path to content directory (required for indexing)'),
4
8
  arc: z.string().optional().describe('Index specific arc only'),
5
9
  episode: z.number().optional().describe('Index specific episode only (requires arc)'),
6
10
  });
@@ -30,11 +34,50 @@ export async function ragIndex(args, tracker, rag) {
30
34
  }
31
35
  }
32
36
  // Convert to embedding format and add to RAG
33
- const embeddingChapters = chapters.map((ch) => ({
34
- id: `${ch.timelineName}-${ch.arcName}-${ch.episodeNumber}-${ch.number}`,
35
- metadata: ch,
36
- content: '', // Content will be loaded by RAG system if needed
37
- }));
37
+ const embeddingChapters = chapters
38
+ .map((ch) => {
39
+ // If contentPath is provided, read actual file content
40
+ if (args.contentPath) {
41
+ try {
42
+ // Find episode directory
43
+ const episodeDir = `ep${String(ch.episodeNumber).padStart(2, '0')}`;
44
+ const arcPath = join(args.contentPath, ch.arcName);
45
+ const episodePath = readdirSync(arcPath, { withFileTypes: true })
46
+ .filter((e) => e.isDirectory() && e.name.startsWith(episodeDir))
47
+ .map((e) => join(arcPath, e.name))[0];
48
+ if (!episodePath) {
49
+ console.error(`Episode directory not found for ${ch.arcName}/ep${ch.episodeNumber}`);
50
+ return null;
51
+ }
52
+ // Find chapter file by episode and chapter number (filename-agnostic for title/pov)
53
+ const chapterPattern = `ep${String(ch.episodeNumber).padStart(2, '0')}-ch${String(ch.number).padStart(3, '0')}-`;
54
+ const chapterFiles = readdirSync(episodePath).filter((f) => f.startsWith(chapterPattern) && f.endsWith('.md'));
55
+ if (chapterFiles.length === 0) {
56
+ console.error(`Chapter file not found for ${ch.arcName}/ep${ch.episodeNumber}/ch${ch.number}`);
57
+ return null;
58
+ }
59
+ const filePath = join(episodePath, chapterFiles[0]);
60
+ const fileContent = readFileSync(filePath, 'utf-8');
61
+ const { content } = parseMarkdown(fileContent);
62
+ return {
63
+ id: `${ch.timelineName}-${ch.arcName}-${ch.episodeNumber}-${ch.number}`,
64
+ metadata: ch,
65
+ content,
66
+ };
67
+ }
68
+ catch (error) {
69
+ console.error(`Error reading chapter ${ch.arcName}/ep${ch.episodeNumber}/ch${ch.number}:`, error);
70
+ return null;
71
+ }
72
+ }
73
+ // Fallback: no content (for tests or when contentPath not provided)
74
+ return {
75
+ id: `${ch.timelineName}-${ch.arcName}-${ch.episodeNumber}-${ch.number}`,
76
+ metadata: ch,
77
+ content: '',
78
+ };
79
+ })
80
+ .filter((ch) => ch !== null);
38
81
  await rag.addChapters(embeddingChapters);
39
82
  return {
40
83
  content: [
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "@echoes-io/mcp-server",
3
3
  "type": "module",
4
- "version": "1.3.1",
4
+ "version": "1.3.3",
5
5
  "description": "Model Context Protocol server for AI integration with Echoes storytelling platform",
6
6
  "scripts": {
7
7
  "dev": "tsx cli/index.ts",
8
8
  "start": "node cli/index.js",
9
9
  "lint": "concurrently npm:lint:* --prefixColors auto",
10
- "lint:fix": "biome check --write",
10
+ "lint:format": "biome check --write",
11
11
  "lint:lockfile": "lockfile-lint --path package-lock.json",
12
12
  "lint:engines": "ls-engines",
13
13
  "lint:publish": "publint --strict",
@@ -82,8 +82,8 @@
82
82
  },
83
83
  "dependencies": {
84
84
  "@echoes-io/books-generator": "^1.0.0",
85
- "@echoes-io/models": "^1.0.0",
86
- "@echoes-io/rag": "^1.1.0",
85
+ "@echoes-io/models": "^1.0.1",
86
+ "@echoes-io/rag": "^1.1.2",
87
87
  "@echoes-io/tracker": "^1.0.0",
88
88
  "@echoes-io/utils": "^1.1.1",
89
89
  "@modelcontextprotocol/sdk": "^1.0.0"