@vue-skuilder/mcp 0.1.8-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.
@@ -0,0 +1,277 @@
1
+ import type { CourseDBInterface } from '@vue-skuilder/db';
2
+
3
+ export interface TagResource {
4
+ name: string;
5
+ description?: string;
6
+ cardCount: number;
7
+ created?: string;
8
+ author?: string;
9
+ metadata?: any;
10
+ }
11
+
12
+ export interface TagsCollection {
13
+ tags: TagResource[];
14
+ total: number;
15
+ stats?: {
16
+ totalTags: number;
17
+ totalTaggedCards: number;
18
+ averageTagsPerCard: number;
19
+ mostUsedTags: { name: string; count: number }[];
20
+ };
21
+ }
22
+
23
+ export interface TagDistribution {
24
+ tagName: string;
25
+ frequency: number;
26
+ percentage: number;
27
+ cardIds: string[];
28
+ }
29
+
30
+ /**
31
+ * Handle tags://all resource - List all available tags
32
+ */
33
+ export async function handleTagsAllResource(
34
+ courseDB: CourseDBInterface
35
+ ): Promise<TagsCollection> {
36
+ try {
37
+ // Get all tag stubs for the course
38
+ const tagStubsResponse = await courseDB.getCourseTagStubs();
39
+ const tagStubs = tagStubsResponse.rows || [];
40
+
41
+ // Transform to TagResource format
42
+ const tags: TagResource[] = [];
43
+ for (const stub of tagStubs) {
44
+ if (stub.doc) {
45
+ // For each tag, we'd need to count associated cards
46
+ // Note: Current CourseDBInterface doesn't have a direct method for this
47
+ // This is a limitation we'll need to address
48
+ tags.push({
49
+ name: (stub.doc as any).name || stub.id,
50
+ description: (stub.doc as any).snippet || `Tag: ${(stub.doc as any).name}`,
51
+ cardCount: (stub.doc as any).taggedCards?.length || 0,
52
+ created: (stub.doc as any).created,
53
+ author: (stub.doc as any).author,
54
+ metadata: (stub.doc as any).metadata
55
+ });
56
+ }
57
+ }
58
+
59
+ return {
60
+ tags,
61
+ total: tags.length,
62
+ stats: {
63
+ totalTags: tags.length,
64
+ totalTaggedCards: 0, // Would need additional queries
65
+ averageTagsPerCard: 0, // Would need calculation
66
+ mostUsedTags: [] // Would need tag usage analysis
67
+ }
68
+ };
69
+
70
+ } catch (error) {
71
+ console.error('Error fetching all tags:', error);
72
+ throw new Error(`Failed to fetch tags: ${error instanceof Error ? error.message : 'Unknown error'}`);
73
+ }
74
+ }
75
+
76
+ /**
77
+ * Handle tags://stats resource - Tag usage statistics
78
+ */
79
+ export async function handleTagsStatsResource(
80
+ courseDB: CourseDBInterface
81
+ ): Promise<TagsCollection['stats']> {
82
+ try {
83
+ // Get all tags
84
+ const tagStubsResponse = await courseDB.getCourseTagStubs();
85
+ const totalTags = tagStubsResponse.rows?.length || 0;
86
+
87
+ // Get course info for context
88
+ const courseInfo = await courseDB.getCourseInfo();
89
+ console.log('Course info loaded for stats:', courseInfo.cardCount);
90
+
91
+ // Note: Comprehensive tag statistics would require additional methods
92
+ // in CourseDBInterface to efficiently query tag-card associations
93
+ return {
94
+ totalTags,
95
+ totalTaggedCards: 0, // Placeholder
96
+ averageTagsPerCard: 0, // Placeholder
97
+ mostUsedTags: [] // Placeholder
98
+ };
99
+
100
+ } catch (error) {
101
+ console.error('Error fetching tag stats:', error);
102
+ throw new Error(`Failed to fetch tag statistics: ${error instanceof Error ? error.message : 'Unknown error'}`);
103
+ }
104
+ }
105
+
106
+ /**
107
+ * Handle tags://[tagName] resource - Specific tag details + card count
108
+ */
109
+ export async function handleTagSpecificResource(
110
+ courseDB: CourseDBInterface,
111
+ tagName: string
112
+ ): Promise<TagResource> {
113
+ try {
114
+ // Get the specific tag
115
+ const tag = await courseDB.getTag(tagName);
116
+ if (!tag) {
117
+ throw new Error(`Tag not found: ${tagName}`);
118
+ }
119
+
120
+ return {
121
+ name: (tag as any).name || tagName,
122
+ description: (tag as any).snippet || `Tag: ${tagName}`,
123
+ cardCount: (tag as any).taggedCards?.length || 0,
124
+ created: (tag as any).created,
125
+ author: (tag as any).author,
126
+ metadata: (tag as any).metadata
127
+ };
128
+
129
+ } catch (error) {
130
+ console.error(`Error fetching tag ${tagName}:`, error);
131
+ throw new Error(`Failed to fetch tag ${tagName}: ${error instanceof Error ? error.message : 'Unknown error'}`);
132
+ }
133
+ }
134
+
135
+ /**
136
+ * Handle tags://union/[tag1]+[tag2] resource - Cards with ANY of these tags
137
+ */
138
+ export async function handleTagsUnionResource(
139
+ courseDB: CourseDBInterface,
140
+ tagsParam: string
141
+ ): Promise<{ cardIds: string[]; tags: string[]; operation: 'union' }> {
142
+ try {
143
+ const tags = tagsParam.split('+').map(tag => tag.trim());
144
+
145
+ if (tags.length === 0) {
146
+ throw new Error('No tags specified for union operation');
147
+ }
148
+
149
+ // Validate all tags exist
150
+ for (const tagName of tags) {
151
+ try {
152
+ await courseDB.getTag(tagName);
153
+ } catch {
154
+ throw new Error(`Tag not found: ${tagName}`);
155
+ }
156
+ }
157
+
158
+ // Note: Union operation would require specialized queries
159
+ // Current CourseDBInterface doesn't provide tag-based card filtering
160
+ return {
161
+ cardIds: [], // Placeholder
162
+ tags,
163
+ operation: 'union'
164
+ };
165
+
166
+ } catch (error) {
167
+ console.error(`Error performing tags union for ${tagsParam}:`, error);
168
+ throw new Error(`Failed to perform tags union: ${error instanceof Error ? error.message : 'Unknown error'}`);
169
+ }
170
+ }
171
+
172
+ /**
173
+ * Handle tags://intersect/[tag1]+[tag2] resource - Cards with ALL these tags
174
+ */
175
+ export async function handleTagsIntersectResource(
176
+ courseDB: CourseDBInterface,
177
+ tagsParam: string
178
+ ): Promise<{ cardIds: string[]; tags: string[]; operation: 'intersect' }> {
179
+ try {
180
+ const tags = tagsParam.split('+').map(tag => tag.trim());
181
+
182
+ if (tags.length === 0) {
183
+ throw new Error('No tags specified for intersect operation');
184
+ }
185
+
186
+ // Validate all tags exist
187
+ for (const tagName of tags) {
188
+ try {
189
+ await courseDB.getTag(tagName);
190
+ } catch {
191
+ throw new Error(`Tag not found: ${tagName}`);
192
+ }
193
+ }
194
+
195
+ // Note: Intersect operation would require specialized queries
196
+ return {
197
+ cardIds: [], // Placeholder
198
+ tags,
199
+ operation: 'intersect'
200
+ };
201
+
202
+ } catch (error) {
203
+ console.error(`Error performing tags intersect for ${tagsParam}:`, error);
204
+ throw new Error(`Failed to perform tags intersect: ${error instanceof Error ? error.message : 'Unknown error'}`);
205
+ }
206
+ }
207
+
208
+ /**
209
+ * Handle tags://exclusive/[tag1]-[tag2] resource - Cards with tag1 but NOT tag2
210
+ */
211
+ export async function handleTagsExclusiveResource(
212
+ courseDB: CourseDBInterface,
213
+ tagsParam: string
214
+ ): Promise<{ cardIds: string[]; includeTag: string; excludeTag: string; operation: 'exclusive' }> {
215
+ try {
216
+ const [includeTag, excludeTag] = tagsParam.split('-').map(tag => tag.trim());
217
+
218
+ if (!includeTag || !excludeTag) {
219
+ throw new Error('Both include and exclude tags must be specified (format: tag1-tag2)');
220
+ }
221
+
222
+ // Validate both tags exist
223
+ try {
224
+ await courseDB.getTag(includeTag);
225
+ await courseDB.getTag(excludeTag);
226
+ } catch (error) {
227
+ throw new Error(`One or both tags not found: ${includeTag}, ${excludeTag}`);
228
+ }
229
+
230
+ // Note: Exclusive operation would require specialized queries
231
+ return {
232
+ cardIds: [], // Placeholder
233
+ includeTag,
234
+ excludeTag,
235
+ operation: 'exclusive'
236
+ };
237
+
238
+ } catch (error) {
239
+ console.error(`Error performing tags exclusive for ${tagsParam}:`, error);
240
+ throw new Error(`Failed to perform tags exclusive: ${error instanceof Error ? error.message : 'Unknown error'}`);
241
+ }
242
+ }
243
+
244
+ /**
245
+ * Handle tags://distribution resource - Tag frequency distribution
246
+ */
247
+ export async function handleTagsDistributionResource(
248
+ courseDB: CourseDBInterface
249
+ ): Promise<{ distribution: TagDistribution[]; totalTags: number; totalCards: number }> {
250
+ try {
251
+ // Get all tags
252
+ const tagStubsResponse = await courseDB.getCourseTagStubs();
253
+ const tags = tagStubsResponse.rows || [];
254
+
255
+ // Get course info
256
+ const courseInfo = await courseDB.getCourseInfo();
257
+
258
+ // Note: Full distribution analysis would require additional queries
259
+ // to map tags to cards and calculate frequencies
260
+ const distribution: TagDistribution[] = tags.map(stub => ({
261
+ tagName: stub.doc?.name || stub.id,
262
+ frequency: 0, // Placeholder
263
+ percentage: 0, // Placeholder
264
+ cardIds: [] // Placeholder
265
+ }));
266
+
267
+ return {
268
+ distribution,
269
+ totalTags: tags.length,
270
+ totalCards: courseInfo.cardCount
271
+ };
272
+
273
+ } catch (error) {
274
+ console.error('Error calculating tag distribution:', error);
275
+ throw new Error(`Failed to calculate tag distribution: ${error instanceof Error ? error.message : 'Unknown error'}`);
276
+ }
277
+ }