@echoes-io/mcp-server 3.0.0 → 4.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 (46) hide show
  1. package/README.md +2 -2
  2. package/package.json +19 -9
  3. package/cli/index.d.ts +0 -2
  4. package/cli/index.js +0 -6
  5. package/lib/prompts/handlers.d.ts +0 -21
  6. package/lib/prompts/handlers.js +0 -107
  7. package/lib/prompts/index.d.ts +0 -2
  8. package/lib/prompts/index.js +0 -2
  9. package/lib/prompts/substitution.d.ts +0 -2
  10. package/lib/prompts/substitution.js +0 -45
  11. package/lib/prompts/validation.d.ts +0 -8
  12. package/lib/prompts/validation.js +0 -20
  13. package/lib/server.d.ts +0 -55
  14. package/lib/server.js +0 -305
  15. package/lib/tools/book-generate.d.ts +0 -20
  16. package/lib/tools/book-generate.js +0 -36
  17. package/lib/tools/chapter-delete.d.ts +0 -15
  18. package/lib/tools/chapter-delete.js +0 -48
  19. package/lib/tools/chapter-info.d.ts +0 -14
  20. package/lib/tools/chapter-info.js +0 -47
  21. package/lib/tools/chapter-insert.d.ts +0 -21
  22. package/lib/tools/chapter-insert.js +0 -104
  23. package/lib/tools/chapter-refresh.d.ts +0 -12
  24. package/lib/tools/chapter-refresh.js +0 -78
  25. package/lib/tools/episode-info.d.ts +0 -13
  26. package/lib/tools/episode-info.js +0 -45
  27. package/lib/tools/episode-update.d.ts +0 -16
  28. package/lib/tools/episode-update.js +0 -42
  29. package/lib/tools/index.d.ts +0 -14
  30. package/lib/tools/index.js +0 -14
  31. package/lib/tools/rag-characters.d.ts +0 -12
  32. package/lib/tools/rag-characters.js +0 -26
  33. package/lib/tools/rag-context.d.ts +0 -16
  34. package/lib/tools/rag-context.js +0 -54
  35. package/lib/tools/rag-index.d.ts +0 -18
  36. package/lib/tools/rag-index.js +0 -89
  37. package/lib/tools/rag-search.d.ts +0 -17
  38. package/lib/tools/rag-search.js +0 -58
  39. package/lib/tools/stats.d.ts +0 -14
  40. package/lib/tools/stats.js +0 -132
  41. package/lib/tools/timeline-sync.d.ts +0 -15
  42. package/lib/tools/timeline-sync.js +0 -217
  43. package/lib/tools/words-count.d.ts +0 -10
  44. package/lib/tools/words-count.js +0 -30
  45. package/lib/utils.d.ts +0 -1
  46. package/lib/utils.js +0 -2
@@ -1,132 +0,0 @@
1
- import { z } from 'zod';
2
- export const statsSchema = z.object({
3
- timeline: z.string().describe('Timeline name'),
4
- arc: z.string().optional().describe('Filter by arc name'),
5
- episode: z.number().optional().describe('Filter by episode number'),
6
- pov: z.string().optional().describe('Filter by POV character'),
7
- });
8
- export async function stats(args, tracker) {
9
- try {
10
- let chapters = [];
11
- // Get chapters based on filters
12
- if (args.arc && args.episode) {
13
- chapters = await tracker.getChapters(args.timeline, args.arc, args.episode);
14
- }
15
- else if (args.arc) {
16
- const episodes = await tracker.getEpisodes(args.timeline, args.arc);
17
- for (const ep of episodes) {
18
- const epChapters = await tracker.getChapters(args.timeline, args.arc, ep.number);
19
- chapters.push(...epChapters);
20
- }
21
- }
22
- else {
23
- const arcs = await tracker.getArcs(args.timeline);
24
- for (const arc of arcs) {
25
- const episodes = await tracker.getEpisodes(args.timeline, arc.name);
26
- for (const ep of episodes) {
27
- const epChapters = await tracker.getChapters(args.timeline, arc.name, ep.number);
28
- chapters.push(...epChapters);
29
- }
30
- }
31
- }
32
- // Filter by POV if specified
33
- if (args.pov) {
34
- chapters = chapters.filter((ch) => ch.pov === args.pov);
35
- }
36
- // Calculate statistics
37
- const totalWords = chapters.reduce((sum, ch) => sum + ch.words, 0);
38
- const totalChapters = chapters.length;
39
- // POV distribution
40
- const povStats = {};
41
- for (const ch of chapters) {
42
- if (!povStats[ch.pov]) {
43
- povStats[ch.pov] = { chapters: 0, words: 0 };
44
- }
45
- povStats[ch.pov].chapters++;
46
- povStats[ch.pov].words += ch.words;
47
- }
48
- // Arc breakdown (if not filtered by arc)
49
- const arcStats = {};
50
- if (!args.arc) {
51
- for (const ch of chapters) {
52
- if (!arcStats[ch.arcName]) {
53
- arcStats[ch.arcName] = { chapters: 0, words: 0, episodes: new Set() };
54
- }
55
- arcStats[ch.arcName].chapters++;
56
- arcStats[ch.arcName].words += ch.words;
57
- arcStats[ch.arcName].episodes.add(ch.episodeNumber);
58
- }
59
- }
60
- // Episode breakdown (if filtered by arc but not episode)
61
- const episodeStats = {};
62
- if (args.arc && !args.episode) {
63
- for (const ch of chapters) {
64
- if (!episodeStats[ch.episodeNumber]) {
65
- episodeStats[ch.episodeNumber] = { chapters: 0, words: 0 };
66
- }
67
- episodeStats[ch.episodeNumber].chapters++;
68
- episodeStats[ch.episodeNumber].words += ch.words;
69
- }
70
- }
71
- const result = {
72
- timeline: args.timeline,
73
- filters: {
74
- arc: args.arc || null,
75
- episode: args.episode || null,
76
- pov: args.pov || null,
77
- },
78
- summary: {
79
- totalChapters,
80
- totalWords,
81
- averageChapterLength: totalChapters > 0 ? Math.round(totalWords / totalChapters) : 0,
82
- },
83
- povDistribution: Object.entries(povStats).map(([pov, stats]) => ({
84
- pov,
85
- chapters: stats.chapters,
86
- words: stats.words,
87
- percentage: totalWords > 0 ? Math.round((stats.words / totalWords) * 100) : 0,
88
- })),
89
- };
90
- if (!args.arc && Object.keys(arcStats).length > 0) {
91
- result.arcBreakdown = Object.entries(arcStats).map(([arc, stats]) => ({
92
- arc,
93
- chapters: stats.chapters,
94
- words: stats.words,
95
- episodes: stats.episodes.size,
96
- }));
97
- }
98
- if (args.arc && !args.episode && Object.keys(episodeStats).length > 0) {
99
- result.episodeBreakdown = Object.entries(episodeStats).map(([episode, stats]) => ({
100
- episode: Number(episode),
101
- chapters: stats.chapters,
102
- words: stats.words,
103
- }));
104
- }
105
- if (totalChapters > 0) {
106
- const sortedByWords = [...chapters].sort((a, b) => b.words - a.words);
107
- result.extremes = {
108
- longest: {
109
- title: sortedByWords[0].title,
110
- pov: sortedByWords[0].pov,
111
- words: sortedByWords[0].words,
112
- },
113
- shortest: {
114
- title: sortedByWords[sortedByWords.length - 1].title,
115
- pov: sortedByWords[sortedByWords.length - 1].pov,
116
- words: sortedByWords[sortedByWords.length - 1].words,
117
- },
118
- };
119
- }
120
- return {
121
- content: [
122
- {
123
- type: 'text',
124
- text: JSON.stringify(result, null, 2),
125
- },
126
- ],
127
- };
128
- }
129
- catch (error) {
130
- throw new Error(`Failed to get stats: ${error instanceof Error ? error.message : 'Unknown error'}`);
131
- }
132
- }
@@ -1,15 +0,0 @@
1
- import type { Tracker } from '@echoes-io/tracker';
2
- import { z } from 'zod';
3
- export declare const timelineSyncSchema: z.ZodObject<{
4
- timeline: z.ZodString;
5
- }, z.core.$strip>;
6
- type TimelineSyncArgs = z.infer<typeof timelineSyncSchema> & {
7
- contentPath: string;
8
- };
9
- export declare function timelineSync(args: TimelineSyncArgs, tracker: Tracker): Promise<{
10
- content: {
11
- type: "text";
12
- text: string;
13
- }[];
14
- }>;
15
- export {};
@@ -1,217 +0,0 @@
1
- import { existsSync, readdirSync, readFileSync } from 'node:fs';
2
- import { extname, join } from 'node:path';
3
- import { getTextStats, parseMarkdown } from '@echoes-io/utils';
4
- import { z } from 'zod';
5
- export const timelineSyncSchema = z.object({
6
- timeline: z.string().describe('Timeline name'),
7
- });
8
- export async function timelineSync(args, tracker) {
9
- try {
10
- let added = 0, updated = 0, deleted = 0, errors = 0;
11
- let timelineRecord = await tracker.getTimeline(args.timeline);
12
- if (!timelineRecord) {
13
- timelineRecord = await tracker.createTimeline({
14
- name: args.timeline,
15
- description: `Timeline ${args.timeline}`,
16
- });
17
- added++;
18
- }
19
- const arcs = readdirSync(args.contentPath, { withFileTypes: true })
20
- .filter((entry) => entry.isDirectory())
21
- .map((entry) => entry.name);
22
- for (const arcName of arcs) {
23
- const arcPath = join(args.contentPath, arcName);
24
- let arc = await tracker.getArc(args.timeline, arcName);
25
- if (!arc) {
26
- arc = await tracker.createArc({
27
- timelineName: args.timeline,
28
- name: arcName,
29
- number: 1,
30
- description: `Arc ${arcName}`,
31
- });
32
- added++;
33
- }
34
- const episodes = readdirSync(arcPath, { withFileTypes: true })
35
- .filter((entry) => entry.isDirectory() && entry.name.startsWith('ep'))
36
- .map((entry) => ({
37
- name: entry.name,
38
- number: Number.parseInt(entry.name.match(/ep(\d+)/)?.[1] || '0', 10),
39
- }));
40
- for (const ep of episodes) {
41
- const episodePath = join(arcPath, ep.name);
42
- let episode = await tracker.getEpisode(args.timeline, arcName, ep.number);
43
- if (!episode) {
44
- try {
45
- episode = await tracker.createEpisode({
46
- timelineName: args.timeline,
47
- arcName: arcName,
48
- number: ep.number,
49
- slug: ep.name,
50
- title: ep.name,
51
- description: `Episode ${ep.number}`,
52
- });
53
- added++;
54
- }
55
- catch (error) {
56
- console.error(`Error creating episode ${arcName}/ep${ep.number}:`, error instanceof Error ? error.message : error);
57
- errors++;
58
- continue;
59
- }
60
- }
61
- const chapters = readdirSync(episodePath)
62
- .filter((file) => extname(file) === '.md')
63
- .map((file) => {
64
- try {
65
- const filePath = join(episodePath, file);
66
- const content = readFileSync(filePath, 'utf-8');
67
- const { metadata, content: markdownContent } = parseMarkdown(content);
68
- const stats = getTextStats(markdownContent);
69
- return {
70
- file: filePath,
71
- metadata,
72
- stats,
73
- };
74
- }
75
- catch (_error) {
76
- console.error(`Error parsing chapter file ${file}:`, _error instanceof Error ? _error.message : _error);
77
- errors++;
78
- return null;
79
- }
80
- })
81
- .filter((ch) => ch !== null);
82
- const partNumbers = new Set(chapters.map((ch) => ch?.metadata.part || 1));
83
- for (const partNum of partNumbers) {
84
- try {
85
- const existingPart = await tracker.getPart(args.timeline, arcName, ep.number, partNum);
86
- if (!existingPart) {
87
- await tracker.createPart({
88
- timelineName: args.timeline,
89
- arcName: arcName,
90
- episodeNumber: ep.number,
91
- number: partNum,
92
- slug: `part-${partNum}`,
93
- title: `Part ${partNum}`,
94
- description: `Part ${partNum} of Episode ${ep.number}`,
95
- });
96
- added++;
97
- }
98
- }
99
- catch (error) {
100
- console.error(`Error creating part ${arcName}/ep${ep.number}/part${partNum}:`, error instanceof Error ? error.message : error);
101
- errors++;
102
- }
103
- }
104
- for (const chapterData of chapters) {
105
- if (!chapterData)
106
- continue;
107
- const chNumber = chapterData.metadata.chapter;
108
- if (!chNumber)
109
- continue;
110
- try {
111
- const existing = await tracker.getChapter(args.timeline, arcName, ep.number, chNumber);
112
- const data = {
113
- timelineName: args.timeline,
114
- arcName: arcName,
115
- episodeNumber: ep.number,
116
- partNumber: chapterData.metadata.part || 1,
117
- number: chNumber,
118
- timeline: args.timeline,
119
- arc: arcName,
120
- episode: ep.number,
121
- part: chapterData.metadata.part || 1,
122
- chapter: chNumber,
123
- pov: chapterData.metadata.pov || 'Unknown',
124
- title: chapterData.metadata.title || 'Untitled',
125
- date: chapterData.metadata.date || '',
126
- summary: chapterData.metadata.summary || '',
127
- location: chapterData.metadata.location || '',
128
- outfit: chapterData.metadata.outfit || '',
129
- kink: chapterData.metadata.kink || '',
130
- words: chapterData.stats.words,
131
- characters: chapterData.stats.characters,
132
- charactersNoSpaces: chapterData.stats.charactersNoSpaces,
133
- paragraphs: chapterData.stats.paragraphs,
134
- sentences: chapterData.stats.sentences,
135
- readingTimeMinutes: Math.ceil(chapterData.stats.words / 200),
136
- };
137
- if (existing) {
138
- await tracker.updateChapter(args.timeline, arcName, ep.number, chNumber, data);
139
- updated++;
140
- }
141
- else {
142
- await tracker.createChapter(data);
143
- added++;
144
- }
145
- }
146
- catch (_error) {
147
- console.error(`Error creating/updating chapter ${arcName}/ep${ep.number}/ch${chNumber}:`, _error instanceof Error ? _error.message : _error);
148
- errors++;
149
- }
150
- }
151
- }
152
- }
153
- try {
154
- const dbArcs = await tracker.getArcs(args.timeline);
155
- for (const arc of dbArcs) {
156
- const dbEpisodes = await tracker.getEpisodes(args.timeline, arc.name);
157
- for (const episode of dbEpisodes) {
158
- const allChapters = await tracker.getChapters(args.timeline, arc.name, episode.number);
159
- for (const dbChapter of allChapters) {
160
- let fileExists = false;
161
- try {
162
- const arcPath = join(args.contentPath, dbChapter.arcName);
163
- if (existsSync(arcPath)) {
164
- const episodeDirs = readdirSync(arcPath, { withFileTypes: true }).filter((entry) => entry.isDirectory() &&
165
- entry.name.startsWith(`ep${dbChapter.episodeNumber.toString().padStart(2, '0')}-`));
166
- for (const episodeDir of episodeDirs) {
167
- const episodePath = join(arcPath, episodeDir.name);
168
- const chapterFiles = readdirSync(episodePath).filter((file) => file.includes(`ch${dbChapter.number.toString().padStart(3, '0')}`) &&
169
- file.endsWith('.md'));
170
- if (chapterFiles.length > 0) {
171
- fileExists = true;
172
- break;
173
- }
174
- }
175
- }
176
- }
177
- catch (_error) {
178
- fileExists = false;
179
- }
180
- if (!fileExists) {
181
- try {
182
- await tracker.deleteChapter(dbChapter.timelineName, dbChapter.arcName, dbChapter.episodeNumber, dbChapter.number);
183
- deleted++;
184
- }
185
- catch (_error) {
186
- errors++;
187
- }
188
- }
189
- }
190
- }
191
- }
192
- }
193
- catch (_error) {
194
- errors++;
195
- }
196
- return {
197
- content: [
198
- {
199
- type: 'text',
200
- text: JSON.stringify({
201
- timeline: args.timeline,
202
- contentPath: args.contentPath,
203
- summary: {
204
- added,
205
- updated,
206
- deleted,
207
- errors,
208
- },
209
- }, null, 2),
210
- },
211
- ],
212
- };
213
- }
214
- catch (error) {
215
- throw new Error(`Failed to sync timeline: ${error instanceof Error ? error.message : 'Unknown error'}`);
216
- }
217
- }
@@ -1,10 +0,0 @@
1
- import { z } from 'zod';
2
- export declare const wordsCountSchema: z.ZodObject<{
3
- file: z.ZodString;
4
- }, z.core.$strip>;
5
- export declare function wordsCount(args: z.infer<typeof wordsCountSchema>): Promise<{
6
- content: {
7
- type: "text";
8
- text: string;
9
- }[];
10
- }>;
@@ -1,30 +0,0 @@
1
- import { readFileSync } from 'node:fs';
2
- import { getTextStats } from '@echoes-io/utils';
3
- import { z } from 'zod';
4
- export const wordsCountSchema = z.object({
5
- file: z.string().describe('Path to markdown file'),
6
- });
7
- export async function wordsCount(args) {
8
- try {
9
- const content = readFileSync(args.file, 'utf-8');
10
- const stats = getTextStats(content);
11
- return {
12
- content: [
13
- {
14
- type: 'text',
15
- text: JSON.stringify({
16
- file: args.file,
17
- words: stats.words,
18
- characters: stats.characters,
19
- charactersNoSpaces: stats.charactersNoSpaces,
20
- paragraphs: stats.paragraphs,
21
- sentences: stats.sentences,
22
- }, null, 2),
23
- },
24
- ],
25
- };
26
- }
27
- catch (error) {
28
- throw new Error(`Failed to count words: ${error instanceof Error ? error.message : 'Unknown error'}`);
29
- }
30
- }
package/lib/utils.d.ts DELETED
@@ -1 +0,0 @@
1
- export {};
package/lib/utils.js DELETED
@@ -1,2 +0,0 @@
1
- export {};
2
- // Removed getTimeline() - timeline is now a required parameter for all tools