@echoes-io/mcp-server 1.2.0 → 1.3.1
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 +9 -7
- package/lib/server.js +12 -4
- package/lib/tools/book-generate.d.ts +23 -0
- package/lib/tools/book-generate.js +38 -0
- package/lib/tools/index.d.ts +1 -0
- package/lib/tools/index.js +1 -0
- package/lib/tools/rag-search.js +1 -1
- package/lib/tools/timeline-sync.js +42 -11
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -40,7 +40,7 @@ Then configure:
|
|
|
40
40
|
"env": {
|
|
41
41
|
"ECHOES_TIMELINE": "your-timeline-name",
|
|
42
42
|
"ECHOES_RAG_PROVIDER": "e5-small",
|
|
43
|
-
"
|
|
43
|
+
"ECHOES_RAG_DB_PATH": "./rag_data.db"
|
|
44
44
|
}
|
|
45
45
|
}
|
|
46
46
|
}
|
|
@@ -52,7 +52,7 @@ Then configure:
|
|
|
52
52
|
**Optional RAG Configuration:**
|
|
53
53
|
- `ECHOES_RAG_PROVIDER`: Embedding provider (`e5-small`, `e5-large`, or `gemini`). Default: `e5-small`
|
|
54
54
|
- `ECHOES_GEMINI_API_KEY`: Required if using `gemini` provider
|
|
55
|
-
- `
|
|
55
|
+
- `ECHOES_RAG_DB_PATH`: SQLite database path. Default: `./rag_data.db`
|
|
56
56
|
|
|
57
57
|
## Available Tools
|
|
58
58
|
|
|
@@ -108,6 +108,13 @@ All tools operate on the timeline specified by the `ECHOES_TIMELINE` environment
|
|
|
108
108
|
- Input: `query`, optional: `arc`, `pov`, `maxChapters`
|
|
109
109
|
- Output: Full chapter content for AI context
|
|
110
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
|
+
|
|
111
118
|
## Development
|
|
112
119
|
|
|
113
120
|
### Scripts
|
|
@@ -144,11 +151,6 @@ npm run lint:fix
|
|
|
144
151
|
- **Testing**: Comprehensive unit and integration tests
|
|
145
152
|
- **Environment**: Uses `ECHOES_TIMELINE` env var for timeline context
|
|
146
153
|
|
|
147
|
-
## Roadmap
|
|
148
|
-
|
|
149
|
-
### Planned Features
|
|
150
|
-
- **Book generation** - LaTeX/PDF compilation tools for creating publishable books from timeline content
|
|
151
|
-
|
|
152
154
|
## License
|
|
153
155
|
|
|
154
156
|
MIT
|
package/lib/server.js
CHANGED
|
@@ -7,7 +7,7 @@ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
|
7
7
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
8
8
|
import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';
|
|
9
9
|
import { zodToJsonSchema } from 'zod-to-json-schema';
|
|
10
|
-
import { 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
|
+
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';
|
|
11
11
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
12
12
|
const pkg = JSON.parse(readFileSync(join(__dirname, '../package.json'), 'utf-8'));
|
|
13
13
|
export function createServer(tracker, rag) {
|
|
@@ -82,6 +82,11 @@ export function createServer(tracker, rag) {
|
|
|
82
82
|
description: 'Retrieve relevant context for AI interactions',
|
|
83
83
|
inputSchema: zodToJsonSchema(ragContextSchema),
|
|
84
84
|
},
|
|
85
|
+
{
|
|
86
|
+
name: 'book-generate',
|
|
87
|
+
description: 'Generate PDF book from timeline content using LaTeX',
|
|
88
|
+
inputSchema: zodToJsonSchema(bookGenerateSchema),
|
|
89
|
+
},
|
|
85
90
|
],
|
|
86
91
|
};
|
|
87
92
|
});
|
|
@@ -112,6 +117,8 @@ export function createServer(tracker, rag) {
|
|
|
112
117
|
return await ragSearch(ragSearchSchema.parse(args), rag);
|
|
113
118
|
case 'rag-context':
|
|
114
119
|
return await ragContext(ragContextSchema.parse(args), rag);
|
|
120
|
+
case 'book-generate':
|
|
121
|
+
return await bookGenerate(bookGenerateSchema.parse(args));
|
|
115
122
|
default:
|
|
116
123
|
throw new Error(`Unknown tool: ${name}`);
|
|
117
124
|
}
|
|
@@ -125,14 +132,15 @@ export async function runServer() {
|
|
|
125
132
|
await tracker.init();
|
|
126
133
|
console.error(`Tracker database initialized: ${dbPath}`);
|
|
127
134
|
// Initialize RAG system
|
|
128
|
-
const
|
|
135
|
+
const ragDbPath = process.env.ECHOES_RAG_DB_PATH ||
|
|
136
|
+
(process.env.NODE_ENV === 'test' ? ':memory:' : './rag_data.db');
|
|
129
137
|
const provider = (process.env.ECHOES_RAG_PROVIDER || 'e5-small');
|
|
130
138
|
const rag = new RAGSystem({
|
|
131
139
|
provider,
|
|
132
|
-
|
|
140
|
+
dbPath: ragDbPath,
|
|
133
141
|
geminiApiKey: process.env.ECHOES_GEMINI_API_KEY,
|
|
134
142
|
});
|
|
135
|
-
console.error(`RAG system initialized: ${
|
|
143
|
+
console.error(`RAG system initialized: ${ragDbPath} (provider: ${provider})`);
|
|
136
144
|
const server = createServer(tracker, rag);
|
|
137
145
|
const transport = new StdioServerTransport();
|
|
138
146
|
await server.connect(transport);
|
|
@@ -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,3 +1,4 @@
|
|
|
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';
|
package/lib/tools/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
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';
|
package/lib/tools/rag-search.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { existsSync, readdirSync } from 'node:fs';
|
|
1
|
+
import { existsSync, readdirSync, readFileSync } from 'node:fs';
|
|
2
2
|
import { extname, join } from 'node:path';
|
|
3
3
|
import { getTextStats, parseMarkdown } from '@echoes-io/utils';
|
|
4
4
|
import { z } from 'zod';
|
|
@@ -43,22 +43,29 @@ export async function timelineSync(args, tracker) {
|
|
|
43
43
|
const episodePath = join(arcPath, ep.name);
|
|
44
44
|
let episode = await tracker.getEpisode(timeline, arcName, ep.number);
|
|
45
45
|
if (!episode) {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
46
|
+
try {
|
|
47
|
+
episode = await tracker.createEpisode({
|
|
48
|
+
timelineName: timeline,
|
|
49
|
+
arcName: arcName,
|
|
50
|
+
number: ep.number,
|
|
51
|
+
slug: ep.name,
|
|
52
|
+
title: ep.name,
|
|
53
|
+
description: `Episode ${ep.number}`,
|
|
54
|
+
});
|
|
55
|
+
added++;
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
console.error(`Error creating episode ${arcName}/ep${ep.number}:`, error instanceof Error ? error.message : error);
|
|
59
|
+
errors++;
|
|
60
|
+
continue; // Skip chapters if episode creation failed
|
|
61
|
+
}
|
|
55
62
|
}
|
|
56
63
|
const chapters = readdirSync(episodePath)
|
|
57
64
|
.filter((file) => extname(file) === '.md')
|
|
58
65
|
.map((file) => {
|
|
59
66
|
try {
|
|
60
67
|
const filePath = join(episodePath, file);
|
|
61
|
-
const content =
|
|
68
|
+
const content = readFileSync(filePath, 'utf-8');
|
|
62
69
|
const { metadata, content: markdownContent } = parseMarkdown(content);
|
|
63
70
|
const stats = getTextStats(markdownContent);
|
|
64
71
|
return {
|
|
@@ -73,6 +80,30 @@ export async function timelineSync(args, tracker) {
|
|
|
73
80
|
}
|
|
74
81
|
})
|
|
75
82
|
.filter((ch) => ch !== null);
|
|
83
|
+
// Collect unique part numbers
|
|
84
|
+
const partNumbers = new Set(chapters.map((ch) => ch?.metadata.part || 1));
|
|
85
|
+
// Create parts if they don't exist
|
|
86
|
+
for (const partNum of partNumbers) {
|
|
87
|
+
try {
|
|
88
|
+
const existingPart = await tracker.getPart(timeline, arcName, ep.number, partNum);
|
|
89
|
+
if (!existingPart) {
|
|
90
|
+
await tracker.createPart({
|
|
91
|
+
timelineName: timeline,
|
|
92
|
+
arcName: arcName,
|
|
93
|
+
episodeNumber: ep.number,
|
|
94
|
+
number: partNum,
|
|
95
|
+
slug: `part-${partNum}`,
|
|
96
|
+
title: `Part ${partNum}`,
|
|
97
|
+
description: `Part ${partNum} of Episode ${ep.number}`,
|
|
98
|
+
});
|
|
99
|
+
added++;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
catch (error) {
|
|
103
|
+
console.error(`Error creating part ${arcName}/ep${ep.number}/part${partNum}:`, error instanceof Error ? error.message : error);
|
|
104
|
+
errors++;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
76
107
|
for (const chapterData of chapters) {
|
|
77
108
|
if (!chapterData)
|
|
78
109
|
continue;
|
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.1",
|
|
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,8 +81,9 @@
|
|
|
81
81
|
"vitest": "^3.2.4"
|
|
82
82
|
},
|
|
83
83
|
"dependencies": {
|
|
84
|
+
"@echoes-io/books-generator": "^1.0.0",
|
|
84
85
|
"@echoes-io/models": "^1.0.0",
|
|
85
|
-
"@echoes-io/rag": "^1.
|
|
86
|
+
"@echoes-io/rag": "^1.1.0",
|
|
86
87
|
"@echoes-io/tracker": "^1.0.0",
|
|
87
88
|
"@echoes-io/utils": "^1.1.1",
|
|
88
89
|
"@modelcontextprotocol/sdk": "^1.0.0"
|