@echoes-io/mcp-server 1.1.0 → 1.3.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/README.md +28 -7
- package/lib/server.d.ts +2 -1
- package/lib/server.js +42 -3
- package/lib/tools/book-generate.d.ts +23 -0
- package/lib/tools/book-generate.js +38 -0
- package/lib/tools/index.d.ts +4 -0
- package/lib/tools/index.js +4 -0
- package/lib/tools/rag-context.d.ts +24 -0
- package/lib/tools/rag-context.js +49 -0
- package/lib/tools/rag-index.d.ts +19 -0
- package/lib/tools/rag-index.js +56 -0
- package/lib/tools/rag-search.d.ts +24 -0
- package/lib/tools/rag-search.js +48 -0
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -38,7 +38,9 @@ Then configure:
|
|
|
38
38
|
"echoes": {
|
|
39
39
|
"command": "echoes-mcp-server",
|
|
40
40
|
"env": {
|
|
41
|
-
"ECHOES_TIMELINE": "your-timeline-name"
|
|
41
|
+
"ECHOES_TIMELINE": "your-timeline-name",
|
|
42
|
+
"ECHOES_RAG_PROVIDER": "e5-small",
|
|
43
|
+
"ECHOES_RAG_DB_PATH": "./rag_data.db"
|
|
42
44
|
}
|
|
43
45
|
}
|
|
44
46
|
}
|
|
@@ -47,6 +49,11 @@ Then configure:
|
|
|
47
49
|
|
|
48
50
|
**Important:** The `ECHOES_TIMELINE` environment variable must be set to specify which timeline to work with. All tools operate on this timeline.
|
|
49
51
|
|
|
52
|
+
**Optional RAG Configuration:**
|
|
53
|
+
- `ECHOES_RAG_PROVIDER`: Embedding provider (`e5-small`, `e5-large`, or `gemini`). Default: `e5-small`
|
|
54
|
+
- `ECHOES_GEMINI_API_KEY`: Required if using `gemini` provider
|
|
55
|
+
- `ECHOES_RAG_DB_PATH`: SQLite database path. Default: `./rag_data.db`
|
|
56
|
+
|
|
50
57
|
## Available Tools
|
|
51
58
|
|
|
52
59
|
All tools operate on the timeline specified by the `ECHOES_TIMELINE` environment variable.
|
|
@@ -88,6 +95,26 @@ All tools operate on the timeline specified by the `ECHOES_TIMELINE` environment
|
|
|
88
95
|
- `arc: "arc1", episode: 1`: Statistics for specific episode
|
|
89
96
|
- `pov: "Alice"`: Statistics for specific POV across timeline
|
|
90
97
|
|
|
98
|
+
### RAG (Semantic Search)
|
|
99
|
+
- **`rag-index`** - Index chapters into vector database for semantic search
|
|
100
|
+
- Input: optional: `arc`, `episode` (to index specific content)
|
|
101
|
+
- Output: Number of chapters indexed
|
|
102
|
+
|
|
103
|
+
- **`rag-search`** - Semantic search across timeline content
|
|
104
|
+
- Input: `query`, optional: `arc`, `pov`, `maxResults`
|
|
105
|
+
- Output: Relevant chapters with similarity scores and previews
|
|
106
|
+
|
|
107
|
+
- **`rag-context`** - Retrieve relevant context for AI interactions
|
|
108
|
+
- Input: `query`, optional: `arc`, `pov`, `maxChapters`
|
|
109
|
+
- Output: Full chapter content for AI context
|
|
110
|
+
|
|
111
|
+
### Book Generation
|
|
112
|
+
- **`book-generate`** - Generate PDF book from timeline content using LaTeX
|
|
113
|
+
- Input: `contentPath`, `outputPath`, optional: `episodes`, `format`
|
|
114
|
+
- Output: PDF book with Victoria Regia template
|
|
115
|
+
- Formats: `a4` (default), `a5`
|
|
116
|
+
- Requirements: pandoc, LaTeX distribution (pdflatex/xelatex/lualatex)
|
|
117
|
+
|
|
91
118
|
## Development
|
|
92
119
|
|
|
93
120
|
### Scripts
|
|
@@ -124,12 +151,6 @@ npm run lint:fix
|
|
|
124
151
|
- **Testing**: Comprehensive unit and integration tests
|
|
125
152
|
- **Environment**: Uses `ECHOES_TIMELINE` env var for timeline context
|
|
126
153
|
|
|
127
|
-
## Roadmap
|
|
128
|
-
|
|
129
|
-
### Planned Features
|
|
130
|
-
- **Book generation** - LaTeX/PDF compilation tools for creating publishable books from timeline content
|
|
131
|
-
- **RAG system** - Retrieval-Augmented Generation integration for AI-assisted writing and content analysis
|
|
132
|
-
|
|
133
154
|
## License
|
|
134
155
|
|
|
135
156
|
MIT
|
package/lib/server.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import { RAGSystem } from '@echoes-io/rag';
|
|
1
2
|
import { Tracker } from '@echoes-io/tracker';
|
|
2
3
|
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
3
|
-
export declare function createServer(tracker: Tracker): Server<{
|
|
4
|
+
export declare function createServer(tracker: Tracker, rag: RAGSystem): Server<{
|
|
4
5
|
method: string;
|
|
5
6
|
params?: {
|
|
6
7
|
[x: string]: unknown;
|
package/lib/server.js
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
import { readFileSync } from 'node:fs';
|
|
2
2
|
import { dirname, join } from 'node:path';
|
|
3
3
|
import { fileURLToPath } from 'node:url';
|
|
4
|
+
import { RAGSystem } from '@echoes-io/rag';
|
|
4
5
|
import { Tracker } from '@echoes-io/tracker';
|
|
5
6
|
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
6
7
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
7
8
|
import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';
|
|
8
9
|
import { zodToJsonSchema } from 'zod-to-json-schema';
|
|
9
|
-
import { chapterDelete, chapterDeleteSchema, chapterInfo, chapterInfoSchema, chapterInsert, chapterInsertSchema, chapterRefresh, chapterRefreshSchema, episodeInfo, episodeInfoSchema, episodeUpdate, episodeUpdateSchema, stats, statsSchema, timelineSync, timelineSyncSchema, wordsCount, wordsCountSchema, } from './tools/index.js';
|
|
10
|
+
import { bookGenerate, bookGenerateSchema, chapterDelete, chapterDeleteSchema, chapterInfo, chapterInfoSchema, chapterInsert, chapterInsertSchema, chapterRefresh, chapterRefreshSchema, episodeInfo, episodeInfoSchema, episodeUpdate, episodeUpdateSchema, ragContext, ragContextSchema, ragIndex, ragIndexSchema, ragSearch, ragSearchSchema, stats, statsSchema, timelineSync, timelineSyncSchema, wordsCount, wordsCountSchema, } from './tools/index.js';
|
|
10
11
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
11
12
|
const pkg = JSON.parse(readFileSync(join(__dirname, '../package.json'), 'utf-8'));
|
|
12
|
-
export function createServer(tracker) {
|
|
13
|
+
export function createServer(tracker, rag) {
|
|
13
14
|
const server = new Server({
|
|
14
15
|
name: pkg.name,
|
|
15
16
|
version: pkg.version,
|
|
@@ -66,6 +67,26 @@ export function createServer(tracker) {
|
|
|
66
67
|
description: 'Get statistics for timeline, arc, episode, or POV',
|
|
67
68
|
inputSchema: zodToJsonSchema(statsSchema),
|
|
68
69
|
},
|
|
70
|
+
{
|
|
71
|
+
name: 'rag-index',
|
|
72
|
+
description: 'Index chapters into RAG vector database for semantic search',
|
|
73
|
+
inputSchema: zodToJsonSchema(ragIndexSchema),
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
name: 'rag-search',
|
|
77
|
+
description: 'Semantic search across timeline content',
|
|
78
|
+
inputSchema: zodToJsonSchema(ragSearchSchema),
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
name: 'rag-context',
|
|
82
|
+
description: 'Retrieve relevant context for AI interactions',
|
|
83
|
+
inputSchema: zodToJsonSchema(ragContextSchema),
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
name: 'book-generate',
|
|
87
|
+
description: 'Generate PDF book from timeline content using LaTeX',
|
|
88
|
+
inputSchema: zodToJsonSchema(bookGenerateSchema),
|
|
89
|
+
},
|
|
69
90
|
],
|
|
70
91
|
};
|
|
71
92
|
});
|
|
@@ -90,6 +111,14 @@ export function createServer(tracker) {
|
|
|
90
111
|
return await timelineSync(timelineSyncSchema.parse(args), tracker);
|
|
91
112
|
case 'stats':
|
|
92
113
|
return await stats(statsSchema.parse(args), tracker);
|
|
114
|
+
case 'rag-index':
|
|
115
|
+
return await ragIndex(ragIndexSchema.parse(args), tracker, rag);
|
|
116
|
+
case 'rag-search':
|
|
117
|
+
return await ragSearch(ragSearchSchema.parse(args), rag);
|
|
118
|
+
case 'rag-context':
|
|
119
|
+
return await ragContext(ragContextSchema.parse(args), rag);
|
|
120
|
+
case 'book-generate':
|
|
121
|
+
return await bookGenerate(bookGenerateSchema.parse(args));
|
|
93
122
|
default:
|
|
94
123
|
throw new Error(`Unknown tool: ${name}`);
|
|
95
124
|
}
|
|
@@ -102,7 +131,17 @@ export async function runServer() {
|
|
|
102
131
|
const tracker = new Tracker(dbPath);
|
|
103
132
|
await tracker.init();
|
|
104
133
|
console.error(`Tracker database initialized: ${dbPath}`);
|
|
105
|
-
|
|
134
|
+
// Initialize RAG system
|
|
135
|
+
const ragDbPath = process.env.ECHOES_RAG_DB_PATH ||
|
|
136
|
+
(process.env.NODE_ENV === 'test' ? ':memory:' : './rag_data.db');
|
|
137
|
+
const provider = (process.env.ECHOES_RAG_PROVIDER || 'e5-small');
|
|
138
|
+
const rag = new RAGSystem({
|
|
139
|
+
provider,
|
|
140
|
+
dbPath: ragDbPath,
|
|
141
|
+
geminiApiKey: process.env.ECHOES_GEMINI_API_KEY,
|
|
142
|
+
});
|
|
143
|
+
console.error(`RAG system initialized: ${ragDbPath} (provider: ${provider})`);
|
|
144
|
+
const server = createServer(tracker, rag);
|
|
106
145
|
const transport = new StdioServerTransport();
|
|
107
146
|
await server.connect(transport);
|
|
108
147
|
console.error('Echoes MCP Server running on stdio');
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export declare const bookGenerateSchema: z.ZodObject<{
|
|
3
|
+
contentPath: z.ZodString;
|
|
4
|
+
outputPath: z.ZodString;
|
|
5
|
+
episodes: z.ZodOptional<z.ZodString>;
|
|
6
|
+
format: z.ZodOptional<z.ZodEnum<["a4", "a5"]>>;
|
|
7
|
+
}, "strip", z.ZodTypeAny, {
|
|
8
|
+
contentPath: string;
|
|
9
|
+
outputPath: string;
|
|
10
|
+
episodes?: string | undefined;
|
|
11
|
+
format?: "a4" | "a5" | undefined;
|
|
12
|
+
}, {
|
|
13
|
+
contentPath: string;
|
|
14
|
+
outputPath: string;
|
|
15
|
+
episodes?: string | undefined;
|
|
16
|
+
format?: "a4" | "a5" | undefined;
|
|
17
|
+
}>;
|
|
18
|
+
export declare function bookGenerate(args: z.infer<typeof bookGenerateSchema>): Promise<{
|
|
19
|
+
content: {
|
|
20
|
+
type: "text";
|
|
21
|
+
text: string;
|
|
22
|
+
}[];
|
|
23
|
+
}>;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { generateBook } from '@echoes-io/books-generator';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { getTimeline } from '../utils.js';
|
|
4
|
+
export const bookGenerateSchema = z.object({
|
|
5
|
+
contentPath: z.string().describe('Path to timeline content folder'),
|
|
6
|
+
outputPath: z.string().describe('Output PDF file path'),
|
|
7
|
+
episodes: z.string().optional().describe('Comma-separated episode numbers (e.g., "1,2,3")'),
|
|
8
|
+
format: z.enum(['a4', 'a5']).optional().describe('Page format (default: a4)'),
|
|
9
|
+
});
|
|
10
|
+
export async function bookGenerate(args) {
|
|
11
|
+
try {
|
|
12
|
+
const timeline = getTimeline();
|
|
13
|
+
await generateBook({
|
|
14
|
+
contentPath: args.contentPath,
|
|
15
|
+
outputPath: args.outputPath,
|
|
16
|
+
timeline,
|
|
17
|
+
episodes: args.episodes,
|
|
18
|
+
format: args.format || 'a4',
|
|
19
|
+
});
|
|
20
|
+
return {
|
|
21
|
+
content: [
|
|
22
|
+
{
|
|
23
|
+
type: 'text',
|
|
24
|
+
text: JSON.stringify({
|
|
25
|
+
success: true,
|
|
26
|
+
timeline,
|
|
27
|
+
outputPath: args.outputPath,
|
|
28
|
+
episodes: args.episodes || 'all',
|
|
29
|
+
format: args.format || 'a4',
|
|
30
|
+
}, null, 2),
|
|
31
|
+
},
|
|
32
|
+
],
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
throw new Error(`Failed to generate book: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
37
|
+
}
|
|
38
|
+
}
|
package/lib/tools/index.d.ts
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
|
+
export { bookGenerate, bookGenerateSchema } from './book-generate.js';
|
|
1
2
|
export { chapterDelete, chapterDeleteSchema } from './chapter-delete.js';
|
|
2
3
|
export { chapterInfo, chapterInfoSchema } from './chapter-info.js';
|
|
3
4
|
export { chapterInsert, chapterInsertSchema } from './chapter-insert.js';
|
|
4
5
|
export { chapterRefresh, chapterRefreshSchema } from './chapter-refresh.js';
|
|
5
6
|
export { episodeInfo, episodeInfoSchema } from './episode-info.js';
|
|
6
7
|
export { episodeUpdate, episodeUpdateSchema } from './episode-update.js';
|
|
8
|
+
export { ragContext, ragContextSchema } from './rag-context.js';
|
|
9
|
+
export { ragIndex, ragIndexSchema } from './rag-index.js';
|
|
10
|
+
export { ragSearch, ragSearchSchema } from './rag-search.js';
|
|
7
11
|
export { stats, statsSchema } from './stats.js';
|
|
8
12
|
export { timelineSync, timelineSyncSchema } from './timeline-sync.js';
|
|
9
13
|
export { wordsCount, wordsCountSchema } from './words-count.js';
|
package/lib/tools/index.js
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
|
+
export { bookGenerate, bookGenerateSchema } from './book-generate.js';
|
|
1
2
|
export { chapterDelete, chapterDeleteSchema } from './chapter-delete.js';
|
|
2
3
|
export { chapterInfo, chapterInfoSchema } from './chapter-info.js';
|
|
3
4
|
export { chapterInsert, chapterInsertSchema } from './chapter-insert.js';
|
|
4
5
|
export { chapterRefresh, chapterRefreshSchema } from './chapter-refresh.js';
|
|
5
6
|
export { episodeInfo, episodeInfoSchema } from './episode-info.js';
|
|
6
7
|
export { episodeUpdate, episodeUpdateSchema } from './episode-update.js';
|
|
8
|
+
export { ragContext, ragContextSchema } from './rag-context.js';
|
|
9
|
+
export { ragIndex, ragIndexSchema } from './rag-index.js';
|
|
10
|
+
export { ragSearch, ragSearchSchema } from './rag-search.js';
|
|
7
11
|
export { stats, statsSchema } from './stats.js';
|
|
8
12
|
export { timelineSync, timelineSyncSchema } from './timeline-sync.js';
|
|
9
13
|
export { wordsCount, wordsCountSchema } from './words-count.js';
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { RAGSystem } from '@echoes-io/rag';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
export declare const ragContextSchema: z.ZodObject<{
|
|
4
|
+
query: z.ZodString;
|
|
5
|
+
arc: z.ZodOptional<z.ZodString>;
|
|
6
|
+
pov: z.ZodOptional<z.ZodString>;
|
|
7
|
+
maxChapters: z.ZodOptional<z.ZodNumber>;
|
|
8
|
+
}, "strip", z.ZodTypeAny, {
|
|
9
|
+
query: string;
|
|
10
|
+
arc?: string | undefined;
|
|
11
|
+
pov?: string | undefined;
|
|
12
|
+
maxChapters?: number | undefined;
|
|
13
|
+
}, {
|
|
14
|
+
query: string;
|
|
15
|
+
arc?: string | undefined;
|
|
16
|
+
pov?: string | undefined;
|
|
17
|
+
maxChapters?: number | undefined;
|
|
18
|
+
}>;
|
|
19
|
+
export declare function ragContext(args: z.infer<typeof ragContextSchema>, rag: RAGSystem): Promise<{
|
|
20
|
+
content: {
|
|
21
|
+
type: "text";
|
|
22
|
+
text: string;
|
|
23
|
+
}[];
|
|
24
|
+
}>;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { getTimeline } from '../utils.js';
|
|
3
|
+
export const ragContextSchema = z.object({
|
|
4
|
+
query: z.string().describe('Context query'),
|
|
5
|
+
arc: z.string().optional().describe('Filter by arc name'),
|
|
6
|
+
pov: z.string().optional().describe('Filter by POV character'),
|
|
7
|
+
maxChapters: z.number().optional().describe('Maximum number of chapters (default: 5)'),
|
|
8
|
+
});
|
|
9
|
+
export async function ragContext(args, rag) {
|
|
10
|
+
try {
|
|
11
|
+
const timeline = getTimeline();
|
|
12
|
+
const results = await rag.getContext({
|
|
13
|
+
query: args.query,
|
|
14
|
+
timeline,
|
|
15
|
+
arc: args.arc,
|
|
16
|
+
pov: args.pov,
|
|
17
|
+
maxChapters: args.maxChapters,
|
|
18
|
+
});
|
|
19
|
+
return {
|
|
20
|
+
content: [
|
|
21
|
+
{
|
|
22
|
+
type: 'text',
|
|
23
|
+
text: JSON.stringify({
|
|
24
|
+
query: args.query,
|
|
25
|
+
timeline,
|
|
26
|
+
filters: {
|
|
27
|
+
arc: args.arc || null,
|
|
28
|
+
pov: args.pov || null,
|
|
29
|
+
},
|
|
30
|
+
context: results.map((r) => ({
|
|
31
|
+
chapter: {
|
|
32
|
+
arc: r.metadata.arcName,
|
|
33
|
+
episode: r.metadata.episodeNumber,
|
|
34
|
+
chapter: r.metadata.number,
|
|
35
|
+
pov: r.metadata.pov,
|
|
36
|
+
title: r.metadata.title,
|
|
37
|
+
},
|
|
38
|
+
similarity: r.similarity,
|
|
39
|
+
content: r.content,
|
|
40
|
+
})),
|
|
41
|
+
}, null, 2),
|
|
42
|
+
},
|
|
43
|
+
],
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
throw new Error(`Failed to get context: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { RAGSystem } from '@echoes-io/rag';
|
|
2
|
+
import type { Tracker } from '@echoes-io/tracker';
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
export declare const ragIndexSchema: z.ZodObject<{
|
|
5
|
+
arc: z.ZodOptional<z.ZodString>;
|
|
6
|
+
episode: z.ZodOptional<z.ZodNumber>;
|
|
7
|
+
}, "strip", z.ZodTypeAny, {
|
|
8
|
+
arc?: string | undefined;
|
|
9
|
+
episode?: number | undefined;
|
|
10
|
+
}, {
|
|
11
|
+
arc?: string | undefined;
|
|
12
|
+
episode?: number | undefined;
|
|
13
|
+
}>;
|
|
14
|
+
export declare function ragIndex(args: z.infer<typeof ragIndexSchema>, tracker: Tracker, rag: RAGSystem): Promise<{
|
|
15
|
+
content: {
|
|
16
|
+
type: "text";
|
|
17
|
+
text: string;
|
|
18
|
+
}[];
|
|
19
|
+
}>;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { getTimeline } from '../utils.js';
|
|
3
|
+
export const ragIndexSchema = z.object({
|
|
4
|
+
arc: z.string().optional().describe('Index specific arc only'),
|
|
5
|
+
episode: z.number().optional().describe('Index specific episode only (requires arc)'),
|
|
6
|
+
});
|
|
7
|
+
export async function ragIndex(args, tracker, rag) {
|
|
8
|
+
try {
|
|
9
|
+
const timeline = getTimeline();
|
|
10
|
+
let chapters = [];
|
|
11
|
+
// Get chapters based on filters
|
|
12
|
+
if (args.arc && args.episode) {
|
|
13
|
+
chapters = await tracker.getChapters(timeline, args.arc, args.episode);
|
|
14
|
+
}
|
|
15
|
+
else if (args.arc) {
|
|
16
|
+
const episodes = await tracker.getEpisodes(timeline, args.arc);
|
|
17
|
+
for (const ep of episodes) {
|
|
18
|
+
const epChapters = await tracker.getChapters(timeline, args.arc, ep.number);
|
|
19
|
+
chapters.push(...epChapters);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
const arcs = await tracker.getArcs(timeline);
|
|
24
|
+
for (const arc of arcs) {
|
|
25
|
+
const episodes = await tracker.getEpisodes(timeline, arc.name);
|
|
26
|
+
for (const ep of episodes) {
|
|
27
|
+
const epChapters = await tracker.getChapters(timeline, arc.name, ep.number);
|
|
28
|
+
chapters.push(...epChapters);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
// 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
|
+
}));
|
|
38
|
+
await rag.addChapters(embeddingChapters);
|
|
39
|
+
return {
|
|
40
|
+
content: [
|
|
41
|
+
{
|
|
42
|
+
type: 'text',
|
|
43
|
+
text: JSON.stringify({
|
|
44
|
+
indexed: embeddingChapters.length,
|
|
45
|
+
timeline,
|
|
46
|
+
arc: args.arc || 'all',
|
|
47
|
+
episode: args.episode || 'all',
|
|
48
|
+
}, null, 2),
|
|
49
|
+
},
|
|
50
|
+
],
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
throw new Error(`Failed to index chapters: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { RAGSystem } from '@echoes-io/rag';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
export declare const ragSearchSchema: z.ZodObject<{
|
|
4
|
+
query: z.ZodString;
|
|
5
|
+
arc: z.ZodOptional<z.ZodString>;
|
|
6
|
+
pov: z.ZodOptional<z.ZodString>;
|
|
7
|
+
maxResults: z.ZodOptional<z.ZodNumber>;
|
|
8
|
+
}, "strip", z.ZodTypeAny, {
|
|
9
|
+
query: string;
|
|
10
|
+
arc?: string | undefined;
|
|
11
|
+
pov?: string | undefined;
|
|
12
|
+
maxResults?: number | undefined;
|
|
13
|
+
}, {
|
|
14
|
+
query: string;
|
|
15
|
+
arc?: string | undefined;
|
|
16
|
+
pov?: string | undefined;
|
|
17
|
+
maxResults?: number | undefined;
|
|
18
|
+
}>;
|
|
19
|
+
export declare function ragSearch(args: z.infer<typeof ragSearchSchema>, rag: RAGSystem): Promise<{
|
|
20
|
+
content: {
|
|
21
|
+
type: "text";
|
|
22
|
+
text: string;
|
|
23
|
+
}[];
|
|
24
|
+
}>;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { getTimeline } from '../utils.js';
|
|
3
|
+
export const ragSearchSchema = z.object({
|
|
4
|
+
query: z.string().describe('Search query'),
|
|
5
|
+
arc: z.string().optional().describe('Filter by arc name'),
|
|
6
|
+
pov: z.string().optional().describe('Filter by POV character'),
|
|
7
|
+
maxResults: z.number().optional().describe('Maximum number of results (default: 10)'),
|
|
8
|
+
});
|
|
9
|
+
export async function ragSearch(args, rag) {
|
|
10
|
+
try {
|
|
11
|
+
const timeline = getTimeline();
|
|
12
|
+
const results = await rag.search(args.query, {
|
|
13
|
+
timeline,
|
|
14
|
+
arc: args.arc,
|
|
15
|
+
pov: args.pov,
|
|
16
|
+
maxResults: args.maxResults,
|
|
17
|
+
});
|
|
18
|
+
return {
|
|
19
|
+
content: [
|
|
20
|
+
{
|
|
21
|
+
type: 'text',
|
|
22
|
+
text: JSON.stringify({
|
|
23
|
+
query: args.query,
|
|
24
|
+
timeline,
|
|
25
|
+
filters: {
|
|
26
|
+
arc: args.arc || null,
|
|
27
|
+
pov: args.pov || null,
|
|
28
|
+
},
|
|
29
|
+
results: results.map((r) => ({
|
|
30
|
+
chapter: {
|
|
31
|
+
arc: r.metadata.arcName,
|
|
32
|
+
episode: r.metadata.episodeNumber,
|
|
33
|
+
chapter: r.metadata.number,
|
|
34
|
+
pov: r.metadata.pov,
|
|
35
|
+
title: r.metadata.title,
|
|
36
|
+
},
|
|
37
|
+
similarity: r.similarity,
|
|
38
|
+
preview: `${r.content.substring(0, 200)}...`,
|
|
39
|
+
})),
|
|
40
|
+
}, null, 2),
|
|
41
|
+
},
|
|
42
|
+
],
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
throw new Error(`Failed to search: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
47
|
+
}
|
|
48
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@echoes-io/mcp-server",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.3.0",
|
|
5
5
|
"description": "Model Context Protocol server for AI integration with Echoes storytelling platform",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"dev": "tsx cli/index.ts",
|
|
@@ -81,9 +81,11 @@
|
|
|
81
81
|
"vitest": "^3.2.4"
|
|
82
82
|
},
|
|
83
83
|
"dependencies": {
|
|
84
|
-
"@echoes-io/
|
|
84
|
+
"@echoes-io/books-generator": "^1.0.0",
|
|
85
85
|
"@echoes-io/models": "^1.0.0",
|
|
86
|
+
"@echoes-io/rag": "^1.1.0",
|
|
86
87
|
"@echoes-io/tracker": "^1.0.0",
|
|
88
|
+
"@echoes-io/utils": "^1.1.1",
|
|
87
89
|
"@modelcontextprotocol/sdk": "^1.0.0"
|
|
88
90
|
}
|
|
89
91
|
}
|