noverload-mcp 0.6.0 → 0.7.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 (125) hide show
  1. package/dist/client.d.ts +8 -0
  2. package/dist/client.d.ts.map +1 -1
  3. package/dist/client.js +77 -0
  4. package/dist/client.js.map +1 -1
  5. package/dist/llm-instructions.d.ts +87 -0
  6. package/dist/llm-instructions.d.ts.map +1 -0
  7. package/dist/llm-instructions.js +257 -0
  8. package/dist/llm-instructions.js.map +1 -0
  9. package/dist/tools/helpers/connections.d.ts +7 -0
  10. package/dist/tools/helpers/connections.d.ts.map +1 -0
  11. package/dist/tools/helpers/connections.js +85 -0
  12. package/dist/tools/helpers/connections.js.map +1 -0
  13. package/dist/tools/helpers/content-processing.d.ts +7 -0
  14. package/dist/tools/helpers/content-processing.d.ts.map +1 -0
  15. package/dist/tools/helpers/content-processing.js +136 -0
  16. package/dist/tools/helpers/content-processing.js.map +1 -0
  17. package/dist/tools/helpers/insights.d.ts +2 -0
  18. package/dist/tools/helpers/insights.d.ts.map +1 -0
  19. package/dist/tools/helpers/insights.js +62 -0
  20. package/dist/tools/helpers/insights.js.map +1 -0
  21. package/dist/tools/implementations/actions.d.ts +4 -0
  22. package/dist/tools/implementations/actions.d.ts.map +1 -0
  23. package/dist/tools/implementations/actions.js +73 -0
  24. package/dist/tools/implementations/actions.js.map +1 -0
  25. package/dist/tools/implementations/batch-get.d.ts +3 -0
  26. package/dist/tools/implementations/batch-get.d.ts.map +1 -0
  27. package/dist/tools/implementations/batch-get.js +102 -0
  28. package/dist/tools/implementations/batch-get.js.map +1 -0
  29. package/dist/tools/implementations/build-narrative.d.ts +3 -0
  30. package/dist/tools/implementations/build-narrative.d.ts.map +1 -0
  31. package/dist/tools/implementations/build-narrative.js +352 -0
  32. package/dist/tools/implementations/build-narrative.js.map +1 -0
  33. package/dist/tools/implementations/estimate-tokens.d.ts +3 -0
  34. package/dist/tools/implementations/estimate-tokens.d.ts.map +1 -0
  35. package/dist/tools/implementations/estimate-tokens.js +54 -0
  36. package/dist/tools/implementations/estimate-tokens.js.map +1 -0
  37. package/dist/tools/implementations/expand-search.d.ts +3 -0
  38. package/dist/tools/implementations/expand-search.d.ts.map +1 -0
  39. package/dist/tools/implementations/expand-search.js +223 -0
  40. package/dist/tools/implementations/expand-search.js.map +1 -0
  41. package/dist/tools/implementations/explore-topic.d.ts +3 -0
  42. package/dist/tools/implementations/explore-topic.d.ts.map +1 -0
  43. package/dist/tools/implementations/explore-topic.js +137 -0
  44. package/dist/tools/implementations/explore-topic.js.map +1 -0
  45. package/dist/tools/implementations/extract-frameworks.d.ts +3 -0
  46. package/dist/tools/implementations/extract-frameworks.d.ts.map +1 -0
  47. package/dist/tools/implementations/extract-frameworks.js +192 -0
  48. package/dist/tools/implementations/extract-frameworks.js.map +1 -0
  49. package/dist/tools/implementations/extract-insights.d.ts +3 -0
  50. package/dist/tools/implementations/extract-insights.d.ts.map +1 -0
  51. package/dist/tools/implementations/extract-insights.js +130 -0
  52. package/dist/tools/implementations/extract-insights.js.map +1 -0
  53. package/dist/tools/implementations/find-connections.d.ts +3 -0
  54. package/dist/tools/implementations/find-connections.d.ts.map +1 -0
  55. package/dist/tools/implementations/find-connections.js +106 -0
  56. package/dist/tools/implementations/find-connections.js.map +1 -0
  57. package/dist/tools/implementations/find-examples.d.ts +3 -0
  58. package/dist/tools/implementations/find-examples.d.ts.map +1 -0
  59. package/dist/tools/implementations/find-examples.js +242 -0
  60. package/dist/tools/implementations/find-examples.js.map +1 -0
  61. package/dist/tools/implementations/get-content.d.ts +3 -0
  62. package/dist/tools/implementations/get-content.d.ts.map +1 -0
  63. package/dist/tools/implementations/get-content.js +96 -0
  64. package/dist/tools/implementations/get-content.js.map +1 -0
  65. package/dist/tools/implementations/goals.d.ts +3 -0
  66. package/dist/tools/implementations/goals.d.ts.map +1 -0
  67. package/dist/tools/implementations/goals.js +22 -0
  68. package/dist/tools/implementations/goals.js.map +1 -0
  69. package/dist/tools/implementations/instructions.d.ts +3 -0
  70. package/dist/tools/implementations/instructions.d.ts.map +1 -0
  71. package/dist/tools/implementations/instructions.js +88 -0
  72. package/dist/tools/implementations/instructions.js.map +1 -0
  73. package/dist/tools/implementations/knowledge-graph.d.ts +3 -0
  74. package/dist/tools/implementations/knowledge-graph.d.ts.map +1 -0
  75. package/dist/tools/implementations/knowledge-graph.js +278 -0
  76. package/dist/tools/implementations/knowledge-graph.js.map +1 -0
  77. package/dist/tools/implementations/list-content.d.ts +3 -0
  78. package/dist/tools/implementations/list-content.d.ts.map +1 -0
  79. package/dist/tools/implementations/list-content.js +87 -0
  80. package/dist/tools/implementations/list-content.js.map +1 -0
  81. package/dist/tools/implementations/plan-query.d.ts +3 -0
  82. package/dist/tools/implementations/plan-query.d.ts.map +1 -0
  83. package/dist/tools/implementations/plan-query.js +63 -0
  84. package/dist/tools/implementations/plan-query.js.map +1 -0
  85. package/dist/tools/implementations/raw-content.d.ts +3 -0
  86. package/dist/tools/implementations/raw-content.d.ts.map +1 -0
  87. package/dist/tools/implementations/raw-content.js +208 -0
  88. package/dist/tools/implementations/raw-content.js.map +1 -0
  89. package/dist/tools/implementations/save-content.d.ts +3 -0
  90. package/dist/tools/implementations/save-content.d.ts.map +1 -0
  91. package/dist/tools/implementations/save-content.js +33 -0
  92. package/dist/tools/implementations/save-content.js.map +1 -0
  93. package/dist/tools/implementations/search.d.ts +3 -0
  94. package/dist/tools/implementations/search.d.ts.map +1 -0
  95. package/dist/tools/implementations/search.js +245 -0
  96. package/dist/tools/implementations/search.js.map +1 -0
  97. package/dist/tools/implementations/similar-content.d.ts +3 -0
  98. package/dist/tools/implementations/similar-content.d.ts.map +1 -0
  99. package/dist/tools/implementations/similar-content.js +64 -0
  100. package/dist/tools/implementations/similar-content.js.map +1 -0
  101. package/dist/tools/implementations/smart-sections.d.ts +3 -0
  102. package/dist/tools/implementations/smart-sections.d.ts.map +1 -0
  103. package/dist/tools/implementations/smart-sections.js +347 -0
  104. package/dist/tools/implementations/smart-sections.js.map +1 -0
  105. package/dist/tools/implementations/synthesize.d.ts +3 -0
  106. package/dist/tools/implementations/synthesize.d.ts.map +1 -0
  107. package/dist/tools/implementations/synthesize.js +148 -0
  108. package/dist/tools/implementations/synthesize.js.map +1 -0
  109. package/dist/tools/implementations/timeline.d.ts +3 -0
  110. package/dist/tools/implementations/timeline.d.ts.map +1 -0
  111. package/dist/tools/implementations/timeline.js +191 -0
  112. package/dist/tools/implementations/timeline.js.map +1 -0
  113. package/dist/tools/index-old.d.ts +16 -0
  114. package/dist/tools/index-old.d.ts.map +1 -0
  115. package/dist/tools/index-old.js +2176 -0
  116. package/dist/tools/index-old.js.map +1 -0
  117. package/dist/tools/index.d.ts +25 -14
  118. package/dist/tools/index.d.ts.map +1 -1
  119. package/dist/tools/index.js +57 -1079
  120. package/dist/tools/index.js.map +1 -1
  121. package/dist/tools/types.d.ts +22 -0
  122. package/dist/tools/types.d.ts.map +1 -0
  123. package/dist/tools/types.js +2 -0
  124. package/dist/tools/types.js.map +1 -0
  125. package/package.json +5 -5
@@ -0,0 +1,2176 @@
1
+ import { z } from "zod";
2
+ import { generateLLMInstructions, planQueryStrategy, estimateTokenUsage } from "../llm-instructions.js";
3
+ export const tools = [
4
+ {
5
+ name: "list_saved_content",
6
+ description: "List saved content from Noverload with full details including titles, summaries, and insights. Optionally filter by status or content type.",
7
+ inputSchema: {
8
+ type: "object",
9
+ properties: {
10
+ status: {
11
+ type: "string",
12
+ enum: ["pending", "processing", "completed", "failed"],
13
+ description: "Filter by processing status",
14
+ },
15
+ contentType: {
16
+ type: "string",
17
+ enum: ["youtube", "x_twitter", "reddit", "article", "pdf"],
18
+ description: "Filter by content type",
19
+ },
20
+ limit: {
21
+ type: "number",
22
+ description: "Maximum number of items to return",
23
+ default: 20,
24
+ },
25
+ },
26
+ },
27
+ modifies: false,
28
+ handler: async (client, args) => {
29
+ const schema = z.object({
30
+ status: z.enum(["pending", "processing", "completed", "failed"]).optional(),
31
+ contentType: z.enum(["youtube", "x_twitter", "reddit", "article", "pdf"]).optional(),
32
+ limit: z.number().optional().default(20),
33
+ });
34
+ const params = schema.parse(args);
35
+ const content = await client.listContent(params);
36
+ // Build detailed response
37
+ let responseText = `# Saved Content Library\n`;
38
+ responseText += `Found ${content.length} items`;
39
+ const filters = [];
40
+ if (params.status)
41
+ filters.push(`status: ${params.status}`);
42
+ if (params.contentType)
43
+ filters.push(`type: ${params.contentType}`);
44
+ if (filters.length > 0) {
45
+ responseText += ` (filtered by ${filters.join(', ')})`;
46
+ }
47
+ responseText += `\n\n`;
48
+ if (content.length > 0) {
49
+ content.forEach((item, idx) => {
50
+ responseText += `## ${idx + 1}. ${item.title || 'Untitled'}\n`;
51
+ responseText += `**ID:** ${item.id}\n`;
52
+ responseText += `**Type:** ${item.contentType} | **Status:** ${item.status}`;
53
+ if (item.tokenCount) {
54
+ responseText += ` | **Size:** ${item.tokenCount.toLocaleString()} tokens`;
55
+ if (item.tokenCount > 50000) {
56
+ responseText += ` 🚨`;
57
+ }
58
+ else if (item.tokenCount > 10000) {
59
+ responseText += ` ⚠️`;
60
+ }
61
+ }
62
+ responseText += `\n**URL:** ${item.url}\n`;
63
+ if (item.summary) {
64
+ const summaryObj = typeof item.summary === 'string'
65
+ ? { text: item.summary }
66
+ : item.summary;
67
+ if (summaryObj.one_sentence) {
68
+ responseText += `**Summary:** ${summaryObj.one_sentence}\n`;
69
+ }
70
+ }
71
+ responseText += `**Saved:** ${new Date(item.createdAt).toLocaleDateString()}\n`;
72
+ responseText += '\n---\n\n';
73
+ });
74
+ }
75
+ else {
76
+ responseText += "No content found matching the specified filters.";
77
+ }
78
+ return {
79
+ content: [
80
+ {
81
+ type: "text",
82
+ text: responseText,
83
+ },
84
+ ],
85
+ data: content,
86
+ };
87
+ },
88
+ },
89
+ {
90
+ name: "get_content_details",
91
+ description: "Get complete details about a specific saved content item including full transcript/article text, AI-generated summary, key insights, and all metadata",
92
+ inputSchema: {
93
+ type: "object",
94
+ properties: {
95
+ contentId: {
96
+ type: "string",
97
+ description: "The ID of the content to retrieve",
98
+ },
99
+ },
100
+ required: ["contentId"],
101
+ },
102
+ modifies: false,
103
+ handler: async (client, args) => {
104
+ const schema = z.object({
105
+ contentId: z.string(),
106
+ });
107
+ const { contentId } = schema.parse(args);
108
+ const content = await client.getContent(contentId);
109
+ // Build comprehensive response with all details
110
+ let responseText = `# Content Details: ${content.title || 'Untitled'}\n\n`;
111
+ responseText += `**ID:** ${content.id}\n`;
112
+ responseText += `**Type:** ${content.contentType}\n`;
113
+ responseText += `**Status:** ${content.status}\n`;
114
+ responseText += `**URL:** ${content.url}\n`;
115
+ responseText += `**Saved:** ${new Date(content.createdAt).toLocaleDateString()}\n`;
116
+ // Show content size information
117
+ if (content.tokenCount) {
118
+ responseText += `**Full Content Size:** ${content.tokenCount.toLocaleString()} tokens`;
119
+ if (content.tokenCount > 50000) {
120
+ responseText += ` 🚨 EXTREMELY LARGE`;
121
+ }
122
+ else if (content.tokenCount > 10000) {
123
+ responseText += ` ⚠️ LARGE`;
124
+ }
125
+ responseText += '\n';
126
+ }
127
+ if (content.tags && content.tags.length > 0) {
128
+ responseText += `**Tags:** ${content.tags.join(', ')}\n`;
129
+ }
130
+ responseText += '\n## Summary & Insights\n';
131
+ if (content.summary) {
132
+ const summaryObj = typeof content.summary === 'string'
133
+ ? { text: content.summary }
134
+ : content.summary;
135
+ if (summaryObj.one_sentence) {
136
+ responseText += `**One-line Summary:** ${summaryObj.one_sentence}\n\n`;
137
+ }
138
+ if (summaryObj.key_insights && Array.isArray(summaryObj.key_insights)) {
139
+ responseText += `**Key Insights:**\n`;
140
+ summaryObj.key_insights.forEach((insight, idx) => {
141
+ responseText += `${idx + 1}. ${insight}\n`;
142
+ });
143
+ responseText += '\n';
144
+ }
145
+ if (summaryObj.main_topics && Array.isArray(summaryObj.main_topics)) {
146
+ responseText += `**Main Topics:** ${summaryObj.main_topics.join(', ')}\n\n`;
147
+ }
148
+ if (summaryObj.actionable_takeaways && Array.isArray(summaryObj.actionable_takeaways)) {
149
+ responseText += `**Actionable Takeaways:**\n`;
150
+ summaryObj.actionable_takeaways.forEach((takeaway, idx) => {
151
+ responseText += `${idx + 1}. ${takeaway}\n`;
152
+ });
153
+ responseText += '\n';
154
+ }
155
+ }
156
+ // Include full text content info and preview
157
+ if (content.rawText) {
158
+ const wordCount = content.rawText.split(/\s+/).length;
159
+ responseText += `## Full Content\n`;
160
+ responseText += `**Word Count:** ${wordCount.toLocaleString()} words\n`;
161
+ // Show a reasonable preview (500 chars is about 100 tokens)
162
+ responseText += `**Preview (first 500 chars):**\n`;
163
+ responseText += `${content.rawText.slice(0, 500)}...\n\n`;
164
+ if (content.tokenCount && content.tokenCount > 10000) {
165
+ responseText += `⚠️ **Note:** The complete ${content.contentType} content (${wordCount.toLocaleString()} words, ~${content.tokenCount.toLocaleString()} tokens) is available in the data field.\n`;
166
+ responseText += `This is a large amount of content that will consume significant context if fully processed.\n`;
167
+ }
168
+ else {
169
+ responseText += `*Note: Complete content is available in the data field for analysis.*\n`;
170
+ }
171
+ }
172
+ return {
173
+ content: [
174
+ {
175
+ type: "text",
176
+ text: responseText,
177
+ },
178
+ ],
179
+ data: content, // Full content including complete rawText
180
+ };
181
+ },
182
+ },
183
+ {
184
+ name: "search_content",
185
+ description: "Advanced search with OR/ANY logic, fuzzy matching, and smart filters. Supports multiple search modes, content type filtering, date ranges, and relevance explanations.",
186
+ inputSchema: {
187
+ type: "object",
188
+ properties: {
189
+ query: {
190
+ type: "string",
191
+ description: "Search query (supports 'term1 OR term2' for any match, quotes for exact phrases)",
192
+ },
193
+ searchMode: {
194
+ type: "string",
195
+ enum: ["any", "all", "phrase"],
196
+ description: "Search logic: 'any' (OR logic), 'all' (AND logic), 'phrase' (exact phrase)",
197
+ default: "any",
198
+ },
199
+ fuzzyMatch: {
200
+ type: "boolean",
201
+ description: "Enable fuzzy/typo-tolerant matching",
202
+ default: true,
203
+ },
204
+ tags: {
205
+ type: "array",
206
+ items: {
207
+ type: "string",
208
+ },
209
+ description: "Filter by tags",
210
+ },
211
+ limit: {
212
+ type: "number",
213
+ description: "Maximum results (default: 10)",
214
+ default: 10,
215
+ },
216
+ includeFullContent: {
217
+ type: "boolean",
218
+ description: "Include full text (WARNING: 10k-100k+ tokens)",
219
+ default: false,
220
+ },
221
+ contentTypes: {
222
+ type: "array",
223
+ items: {
224
+ type: "string",
225
+ enum: ["youtube", "x_twitter", "reddit", "article", "pdf"],
226
+ },
227
+ description: "Filter by content types",
228
+ },
229
+ dateFrom: {
230
+ type: "string",
231
+ description: "Content saved after this date (ISO 8601)",
232
+ },
233
+ dateTo: {
234
+ type: "string",
235
+ description: "Content saved before this date (ISO 8601)",
236
+ },
237
+ excludeDomains: {
238
+ type: "array",
239
+ items: {
240
+ type: "string",
241
+ },
242
+ description: "Domains to exclude",
243
+ },
244
+ showRelevanceExplanation: {
245
+ type: "boolean",
246
+ description: "Show why each result matched",
247
+ default: true,
248
+ },
249
+ },
250
+ required: ["query"],
251
+ },
252
+ modifies: false,
253
+ handler: async (client, args) => {
254
+ const schema = z.object({
255
+ query: z.string(),
256
+ searchMode: z.enum(["any", "all", "phrase"]).optional().default("any"),
257
+ fuzzyMatch: z.boolean().optional().default(true),
258
+ tags: z.array(z.string()).optional(),
259
+ limit: z.number().optional().default(10),
260
+ includeFullContent: z.boolean().optional().default(false),
261
+ contentTypes: z.array(z.enum(["youtube", "x_twitter", "reddit", "article", "pdf"])).optional(),
262
+ dateFrom: z.string().optional(),
263
+ dateTo: z.string().optional(),
264
+ excludeDomains: z.array(z.string()).optional(),
265
+ showRelevanceExplanation: z.boolean().optional().default(true),
266
+ });
267
+ const params = schema.parse(args);
268
+ // Enhance search query based on mode
269
+ let enhancedQuery = params.query;
270
+ if (params.searchMode === "any" && !params.query.includes("OR")) {
271
+ // Convert space-separated terms to OR query for broader matching
272
+ const terms = params.query.split(/\s+/).filter(t => t.length > 2);
273
+ if (terms.length > 1) {
274
+ enhancedQuery = terms.join(" OR ");
275
+ }
276
+ }
277
+ const results = await client.searchContent(enhancedQuery, {
278
+ ...params,
279
+ enableConceptExpansion: params.fuzzyMatch, // Use fuzzy matching as concept expansion
280
+ });
281
+ // Format response with visual indicators
282
+ let responseText = `# 🔍 Search Results: "${params.query}"\n`;
283
+ responseText += `**Mode:** ${params.searchMode.toUpperCase()}${params.fuzzyMatch ? " with fuzzy matching" : ""}\n`;
284
+ responseText += `**Found:** ${results.length} results`;
285
+ // Show active filters
286
+ const filters = [];
287
+ if (params.tags?.length)
288
+ filters.push(`📌 tags: ${params.tags.join(", ")}`);
289
+ if (params.contentTypes?.length)
290
+ filters.push(`📁 types: ${params.contentTypes.join(", ")}`);
291
+ if (params.dateFrom || params.dateTo)
292
+ filters.push(`📅 date range`);
293
+ if (params.excludeDomains?.length)
294
+ filters.push(`🚫 excluded: ${params.excludeDomains.join(", ")}`);
295
+ if (filters.length > 0) {
296
+ responseText += `\n**Filters:** ${filters.join(" | ")}`;
297
+ }
298
+ if (params.includeFullContent) {
299
+ responseText += `\n⚠️ **Full content included** - consuming significant tokens`;
300
+ }
301
+ responseText += `\n\n`;
302
+ if (results.length > 0) {
303
+ // Add search suggestions if no perfect matches
304
+ if (results.length < 3) {
305
+ responseText += `💡 **Tip:** Try broader search with 'any' mode or disable filters for more results\n\n`;
306
+ }
307
+ results.forEach((result, idx) => {
308
+ // Visual indicators for content type
309
+ const typeIcons = {
310
+ youtube: "📺",
311
+ x_twitter: "𝕏",
312
+ reddit: "🔗",
313
+ article: "📄",
314
+ pdf: "📑"
315
+ };
316
+ const typeIcon = typeIcons[result.contentType] || "📄";
317
+ // Relevance indicator
318
+ let relevanceIcon = "";
319
+ if (result.relevanceScore) {
320
+ if (result.relevanceScore > 0.8)
321
+ relevanceIcon = "🎯";
322
+ else if (result.relevanceScore > 0.6)
323
+ relevanceIcon = "✅";
324
+ else if (result.relevanceScore > 0.4)
325
+ relevanceIcon = "➡️";
326
+ }
327
+ responseText += `## ${idx + 1}. ${typeIcon} ${result.title || 'Untitled'} ${relevanceIcon}\n`;
328
+ responseText += `**ID:** \`${result.id}\`\n`;
329
+ responseText += `**URL:** ${result.url}\n`;
330
+ // Show relevance explanation if available
331
+ if (params.showRelevanceExplanation) {
332
+ if (result.matchReason && Array.isArray(result.matchReason)) {
333
+ responseText += `**Why matched:** Matched in ${result.matchReason.join(", ")}\n`;
334
+ }
335
+ else if (result.relevanceScore) {
336
+ const percentage = (result.relevanceScore * 100).toFixed(0);
337
+ responseText += `**Relevance:** ${percentage}% match\n`;
338
+ }
339
+ }
340
+ // Content size indicator with better formatting
341
+ if (result.tokenCount) {
342
+ const sizeIcon = result.tokenCount > 50000 ? "🚨" :
343
+ result.tokenCount > 10000 ? "⚠️" :
344
+ result.tokenCount > 5000 ? "📊" : "📝";
345
+ responseText += `**Size:** ${sizeIcon} ~${result.tokenCount.toLocaleString()} tokens`;
346
+ if (params.includeFullContent) {
347
+ responseText += ` (full content included)`;
348
+ }
349
+ responseText += `\n`;
350
+ }
351
+ // Tags with better formatting
352
+ if (result.tags && result.tags.length > 0) {
353
+ responseText += `**Tags:** ${result.tags.map((t) => `\`${t}\``).join(' ')}\n`;
354
+ }
355
+ // Summary section with structure
356
+ if (result.summary) {
357
+ const summaryObj = typeof result.summary === 'string'
358
+ ? { text: result.summary }
359
+ : result.summary;
360
+ if (summaryObj.one_sentence) {
361
+ responseText += `\n📝 **Summary:** ${summaryObj.one_sentence}\n`;
362
+ }
363
+ // Key insights with bullets
364
+ if (summaryObj.key_insights && Array.isArray(summaryObj.key_insights) && summaryObj.key_insights.length > 0) {
365
+ responseText += `\n💡 **Key Insights:**\n`;
366
+ summaryObj.key_insights.slice(0, 3).forEach((insight) => {
367
+ responseText += ` • ${insight}\n`;
368
+ });
369
+ }
370
+ // Actionable takeaways
371
+ if (summaryObj.actionable_takeaways && Array.isArray(summaryObj.actionable_takeaways) && summaryObj.actionable_takeaways.length > 0) {
372
+ responseText += `\n🎯 **Actions:**\n`;
373
+ summaryObj.actionable_takeaways.slice(0, 2).forEach((action) => {
374
+ responseText += ` → ${action}\n`;
375
+ });
376
+ }
377
+ }
378
+ responseText += '\n---\n\n';
379
+ });
380
+ // Smart token usage summary
381
+ if (!params.includeFullContent) {
382
+ let totalAvailable = 0;
383
+ results.forEach((r) => {
384
+ if (r.tokenCount)
385
+ totalAvailable += r.tokenCount;
386
+ });
387
+ if (totalAvailable > 0) {
388
+ responseText += `\n📊 **Content Overview:**\n`;
389
+ responseText += `- Showing: Summaries and metadata (minimal tokens)\n`;
390
+ responseText += `- Available: ~${totalAvailable.toLocaleString()} tokens of full content\n`;
391
+ responseText += `- To access: Use \`get_content_details\` for specific items\n`;
392
+ }
393
+ }
394
+ // Search quality feedback
395
+ if (results.length === params.limit) {
396
+ responseText += `\n💡 **Note:** Showing top ${params.limit} results. More may be available.\n`;
397
+ }
398
+ }
399
+ else {
400
+ // No results - provide helpful suggestions
401
+ responseText += "## No Results Found 😔\n\n";
402
+ responseText += "**Try these approaches:**\n";
403
+ responseText += "1. 🔄 Switch to `searchMode: 'any'` for broader matching\n";
404
+ responseText += "2. ✏️ Check spelling or use simpler terms\n";
405
+ responseText += "3. 🎯 Remove filters (dates, types, domains)\n";
406
+ responseText += "4. 🔍 Use more general keywords\n";
407
+ // Suggest alternative queries
408
+ const terms = params.query.split(/\s+/);
409
+ if (terms.length > 1) {
410
+ responseText += `\n**Alternative queries:**\n`;
411
+ responseText += `- "${terms[0]}" (single term)\n`;
412
+ responseText += `- "${terms.slice(0, 2).join(' ')}" (first two terms)\n`;
413
+ }
414
+ }
415
+ return {
416
+ content: [
417
+ {
418
+ type: "text",
419
+ text: responseText,
420
+ },
421
+ ],
422
+ data: results,
423
+ };
424
+ },
425
+ },
426
+ {
427
+ name: "save_content",
428
+ description: "Save a new URL to Noverload for processing",
429
+ inputSchema: {
430
+ type: "object",
431
+ properties: {
432
+ url: {
433
+ type: "string",
434
+ description: "URL to save (YouTube, X/Twitter, Reddit, article, or PDF)",
435
+ },
436
+ },
437
+ required: ["url"],
438
+ },
439
+ modifies: true,
440
+ handler: async (client, args) => {
441
+ const schema = z.object({
442
+ url: z.string().url(),
443
+ });
444
+ const { url } = schema.parse(args);
445
+ const content = await client.saveContent(url);
446
+ return {
447
+ content: [
448
+ {
449
+ type: "text",
450
+ text: content.title ? `Saved: ${content.title}` : `Saved content from: ${new URL(url).hostname}`,
451
+ },
452
+ ],
453
+ data: content,
454
+ };
455
+ },
456
+ },
457
+ {
458
+ name: "list_actions",
459
+ description: "List action items extracted from saved content",
460
+ inputSchema: {
461
+ type: "object",
462
+ properties: {
463
+ contentId: {
464
+ type: "string",
465
+ description: "Filter by content ID",
466
+ },
467
+ goalId: {
468
+ type: "string",
469
+ description: "Filter by goal ID",
470
+ },
471
+ completed: {
472
+ type: "boolean",
473
+ description: "Filter by completion status",
474
+ },
475
+ },
476
+ },
477
+ modifies: false,
478
+ handler: async (client, args) => {
479
+ const schema = z.object({
480
+ contentId: z.string().optional(),
481
+ goalId: z.string().optional(),
482
+ completed: z.boolean().optional(),
483
+ });
484
+ const params = schema.parse(args);
485
+ const actions = await client.listActions(params);
486
+ return {
487
+ content: [
488
+ {
489
+ type: "text",
490
+ text: `Found ${actions.length} actions`,
491
+ },
492
+ ],
493
+ data: actions,
494
+ };
495
+ },
496
+ },
497
+ {
498
+ name: "complete_action",
499
+ description: "Mark an action item as completed",
500
+ inputSchema: {
501
+ type: "object",
502
+ properties: {
503
+ actionId: {
504
+ type: "string",
505
+ description: "The ID of the action to complete",
506
+ },
507
+ },
508
+ required: ["actionId"],
509
+ },
510
+ modifies: true,
511
+ handler: async (client, args) => {
512
+ const schema = z.object({
513
+ actionId: z.string(),
514
+ });
515
+ const { actionId } = schema.parse(args);
516
+ const action = await client.completeAction(actionId);
517
+ return {
518
+ content: [
519
+ {
520
+ type: "text",
521
+ text: `Completed action: ${action.title}`,
522
+ },
523
+ ],
524
+ data: action,
525
+ };
526
+ },
527
+ },
528
+ {
529
+ name: "list_goals",
530
+ description: "List user's goals (Health, Wealth, Relationships)",
531
+ inputSchema: {
532
+ type: "object",
533
+ properties: {},
534
+ },
535
+ modifies: false,
536
+ handler: async (client) => {
537
+ const goals = await client.listGoals();
538
+ return {
539
+ content: [
540
+ {
541
+ type: "text",
542
+ text: `Found ${goals.length} goals`,
543
+ },
544
+ ],
545
+ data: goals,
546
+ };
547
+ },
548
+ },
549
+ {
550
+ name: "estimate_search_tokens",
551
+ description: "Estimate token usage before performing a search. Helps manage context window and API costs.",
552
+ inputSchema: {
553
+ type: "object",
554
+ properties: {
555
+ query: {
556
+ type: "string",
557
+ description: "Search query to estimate",
558
+ },
559
+ limit: {
560
+ type: "number",
561
+ description: "Number of results to estimate for",
562
+ default: 10,
563
+ },
564
+ },
565
+ required: ["query"],
566
+ },
567
+ modifies: false,
568
+ handler: async (client, args) => {
569
+ const schema = z.object({
570
+ query: z.string(),
571
+ limit: z.number().optional().default(10),
572
+ });
573
+ const params = schema.parse(args);
574
+ const estimate = await client.estimateSearchTokens(params.query, params.limit);
575
+ let responseText = `# Token Estimate for: "${params.query}"\n\n`;
576
+ responseText += `**Total Estimated Tokens:** ${estimate.totals?.estimatedTokens || 0}\n`;
577
+ responseText += `**Recommendation:** ${estimate.recommendation || "Unknown"}\n\n`;
578
+ if (estimate.costEstimate) {
579
+ responseText += `## Estimated Costs\n`;
580
+ responseText += `- GPT-4: ${estimate.costEstimate.gpt4}\n`;
581
+ responseText += `- GPT-3.5: ${estimate.costEstimate.gpt35}\n`;
582
+ responseText += `- Claude 3: ${estimate.costEstimate.claude3}\n\n`;
583
+ }
584
+ if (estimate.estimates && estimate.estimates.length > 0) {
585
+ responseText += `## Content Breakdown\n`;
586
+ estimate.estimates.forEach((item, idx) => {
587
+ responseText += `${idx + 1}. ${item.title || "Untitled"} - ${item.estimatedTokens} tokens\n`;
588
+ });
589
+ }
590
+ return {
591
+ content: [
592
+ {
593
+ type: "text",
594
+ text: responseText,
595
+ },
596
+ ],
597
+ data: estimate,
598
+ };
599
+ },
600
+ },
601
+ {
602
+ name: "synthesize_content",
603
+ description: "Analyze multiple content sources to generate actionable insights, find patterns, connections, and contradictions. Creates structured synthesis with timeline, next steps, and confidence scores.",
604
+ inputSchema: {
605
+ type: "object",
606
+ properties: {
607
+ query: {
608
+ type: "string",
609
+ description: "Topic or question to synthesize insights about",
610
+ },
611
+ contentIds: {
612
+ type: "array",
613
+ items: {
614
+ type: "string",
615
+ },
616
+ description: "Specific content IDs to analyze (optional, otherwise uses search)",
617
+ },
618
+ synthesisMode: {
619
+ type: "string",
620
+ enum: ["overview", "deep", "actionable", "comparison"],
621
+ description: "Type of synthesis to perform (default: actionable)",
622
+ default: "actionable",
623
+ },
624
+ findContradictions: {
625
+ type: "boolean",
626
+ description: "Look for contradictions between sources",
627
+ default: false,
628
+ },
629
+ findConnections: {
630
+ type: "boolean",
631
+ description: "Find connections and patterns across sources",
632
+ default: true,
633
+ },
634
+ maxSources: {
635
+ type: "number",
636
+ description: "Maximum number of sources to analyze",
637
+ default: 10,
638
+ },
639
+ },
640
+ required: ["query"],
641
+ },
642
+ modifies: false,
643
+ handler: async (client, args) => {
644
+ const schema = z.object({
645
+ query: z.string(),
646
+ contentIds: z.array(z.string()).optional(),
647
+ synthesisMode: z.enum(["overview", "deep", "actionable", "comparison"]).optional().default("actionable"),
648
+ findContradictions: z.boolean().optional().default(false),
649
+ findConnections: z.boolean().optional().default(true),
650
+ maxSources: z.number().optional().default(10),
651
+ });
652
+ const params = schema.parse(args);
653
+ const result = await client.synthesizeContent(params);
654
+ // Handle the API v2 response format
655
+ const synthesis = result.synthesis || result;
656
+ const metadata = result.metadata || {};
657
+ const sources = result.sources || [];
658
+ let responseText = `# 🎯 Synthesis: "${params.query}"\n`;
659
+ responseText += `**Mode:** ${synthesis.mode || params.synthesisMode} | **Sources:** ${metadata.sourceCount || sources.length}`;
660
+ // Add confidence indicator if available
661
+ if (metadata.confidence) {
662
+ const confidenceIcon = metadata.confidence >= 80 ? "🟢" : metadata.confidence >= 60 ? "🟡" : "🔴";
663
+ responseText += ` | **Confidence:** ${confidenceIcon} ${metadata.confidence}%`;
664
+ }
665
+ responseText += `\n\n`;
666
+ // Summary Section
667
+ if (synthesis.summary) {
668
+ responseText += `## 📋 Summary\n${synthesis.summary}\n\n`;
669
+ }
670
+ // Insights Section (most important)
671
+ if (synthesis.insights && synthesis.insights.length > 0) {
672
+ responseText += `## 💡 Key Insights\n`;
673
+ synthesis.insights.forEach((insight, idx) => {
674
+ // Handle both string and object insights
675
+ if (typeof insight === 'string') {
676
+ responseText += `${idx + 1}. ${insight}\n`;
677
+ }
678
+ else if (insight.text) {
679
+ responseText += `${idx + 1}. **${insight.text}**`;
680
+ if (insight.sourceTitle) {
681
+ responseText += ` *(from: ${insight.sourceTitle})*`;
682
+ }
683
+ responseText += `\n`;
684
+ }
685
+ });
686
+ responseText += `\n`;
687
+ }
688
+ // Themes Section
689
+ if (synthesis.themes && synthesis.themes.length > 0) {
690
+ responseText += `## 🎨 Major Themes\n`;
691
+ synthesis.themes.forEach((theme) => {
692
+ if (typeof theme === 'string') {
693
+ responseText += `- ${theme}\n`;
694
+ }
695
+ else if (theme.theme) {
696
+ responseText += `\n### ${theme.theme}`;
697
+ if (theme.prevalence) {
698
+ responseText += ` (strength: ${theme.prevalence})`;
699
+ }
700
+ responseText += `\n`;
701
+ if (theme.concepts && theme.concepts.length > 0) {
702
+ responseText += `Related concepts: `;
703
+ responseText += theme.concepts.slice(0, 3).map((c) => typeof c === 'string' ? c : c.concept).join(', ');
704
+ responseText += `\n`;
705
+ }
706
+ }
707
+ });
708
+ responseText += `\n`;
709
+ }
710
+ // Priorities (for actionable mode)
711
+ if (synthesis.priorities) {
712
+ responseText += `## 🎯 Priorities\n`;
713
+ if (synthesis.priorities.high && synthesis.priorities.high.length > 0) {
714
+ responseText += `\n### 🔴 High Priority\n`;
715
+ synthesis.priorities.high.forEach((item) => {
716
+ responseText += `- ${item}\n`;
717
+ });
718
+ }
719
+ if (synthesis.priorities.medium && synthesis.priorities.medium.length > 0) {
720
+ responseText += `\n### 🟡 Medium Priority\n`;
721
+ synthesis.priorities.medium.forEach((item) => {
722
+ responseText += `- ${item}\n`;
723
+ });
724
+ }
725
+ if (synthesis.priorities.low && synthesis.priorities.low.length > 0) {
726
+ responseText += `\n### 🟢 Low Priority\n`;
727
+ synthesis.priorities.low.forEach((item) => {
728
+ responseText += `- ${item}\n`;
729
+ });
730
+ }
731
+ responseText += `\n`;
732
+ }
733
+ // Implementation Steps (for actionable mode)
734
+ if (synthesis.implementation && synthesis.implementation.length > 0) {
735
+ responseText += `## 📝 Implementation Steps\n`;
736
+ synthesis.implementation.forEach((step, idx) => {
737
+ responseText += `${idx + 1}. ${step}\n`;
738
+ });
739
+ responseText += `\n`;
740
+ }
741
+ // Source Comparison (for comparative mode)
742
+ if (synthesis.sourceComparison && synthesis.sourceComparison.length > 0) {
743
+ responseText += `## 📊 Source Comparison\n`;
744
+ synthesis.sourceComparison.forEach((source) => {
745
+ responseText += `\n### ${source.source}\n`;
746
+ responseText += `- **Insights:** ${source.insightCount}\n`;
747
+ if (source.mainThemes && source.mainThemes.length > 0) {
748
+ responseText += `- **Main themes:** ${source.mainThemes.join(', ')}\n`;
749
+ }
750
+ });
751
+ responseText += `\n`;
752
+ }
753
+ // Commonalities and Differences
754
+ if (synthesis.commonalities && synthesis.commonalities.length > 0) {
755
+ responseText += `## ✅ Common Themes Across Sources\n`;
756
+ synthesis.commonalities.forEach((theme) => {
757
+ responseText += `- ${theme}\n`;
758
+ });
759
+ responseText += `\n`;
760
+ }
761
+ if (synthesis.differences && synthesis.differences.length > 0) {
762
+ responseText += `## 🔀 Unique Perspectives\n`;
763
+ synthesis.differences.forEach((diff) => {
764
+ responseText += `- **${diff.source}:** ${diff.themes.join(', ')}\n`;
765
+ });
766
+ responseText += `\n`;
767
+ }
768
+ // Connections
769
+ if (synthesis.connections && synthesis.connections.length > 0) {
770
+ responseText += `## 🔗 Connections & Patterns\n`;
771
+ synthesis.connections.forEach((conn) => {
772
+ if (conn.concept && conn.sources) {
773
+ responseText += `- **${conn.concept}** appears in: ${conn.sources.join(', ')}`;
774
+ if (conn.strength) {
775
+ responseText += ` (strength: ${conn.strength})`;
776
+ }
777
+ responseText += `\n`;
778
+ }
779
+ else if (conn.pattern) {
780
+ responseText += `- **${conn.pattern}**`;
781
+ if (conn.implication) {
782
+ responseText += `: ${conn.implication}`;
783
+ }
784
+ responseText += `\n`;
785
+ }
786
+ });
787
+ responseText += `\n`;
788
+ }
789
+ // Contradictions
790
+ if (synthesis.contradictions && synthesis.contradictions.length > 0) {
791
+ responseText += `## ⚡ Contradictions Found\n`;
792
+ synthesis.contradictions.forEach((contra) => {
793
+ if (contra.source1 && contra.statement1) {
794
+ responseText += `\n### Potential Contradiction\n`;
795
+ responseText += `- **${contra.source1}:** "${contra.statement1}"\n`;
796
+ responseText += `- **${contra.source2}:** "${contra.statement2}"\n`;
797
+ if (contra.type) {
798
+ responseText += `- **Type:** ${contra.type}\n`;
799
+ }
800
+ }
801
+ else if (contra.topic) {
802
+ responseText += `\n### ${contra.topic}\n`;
803
+ if (contra.viewpoints) {
804
+ contra.viewpoints.forEach((vp) => {
805
+ responseText += `- **${vp.position}:** ${vp.argument}\n`;
806
+ });
807
+ }
808
+ if (contra.resolution) {
809
+ responseText += `**Suggested resolution:** ${contra.resolution}\n`;
810
+ }
811
+ }
812
+ });
813
+ responseText += `\n`;
814
+ }
815
+ // Timeline
816
+ if (synthesis.timeline && synthesis.timeline.length > 0) {
817
+ responseText += `## ⏱️ Timeline\n`;
818
+ synthesis.timeline.forEach((event) => {
819
+ if (event.date) {
820
+ responseText += `- **${event.date}:** ${event.event}`;
821
+ if (event.source) {
822
+ responseText += ` *(${event.source})*`;
823
+ }
824
+ responseText += `\n`;
825
+ }
826
+ else if (event.phase) {
827
+ responseText += `\n### ${event.phase}`;
828
+ if (event.duration) {
829
+ responseText += ` (${event.duration})`;
830
+ }
831
+ responseText += `\n`;
832
+ if (event.activities && event.activities.length > 0) {
833
+ event.activities.forEach((activity) => {
834
+ responseText += `- ${activity}\n`;
835
+ });
836
+ }
837
+ }
838
+ });
839
+ responseText += `\n`;
840
+ }
841
+ // Action Plan
842
+ if (synthesis.actionPlan) {
843
+ responseText += `## 🚀 Action Plan\n`;
844
+ if (synthesis.actionPlan.summary) {
845
+ responseText += `${synthesis.actionPlan.summary}\n\n`;
846
+ }
847
+ if (synthesis.actionPlan.nextAction) {
848
+ responseText += `**🎯 Next Action:** ${synthesis.actionPlan.nextAction.action || synthesis.actionPlan.nextAction}\n\n`;
849
+ }
850
+ if (synthesis.actionPlan.steps && synthesis.actionPlan.steps.length > 0) {
851
+ responseText += `### Steps\n`;
852
+ synthesis.actionPlan.steps.forEach((step, idx) => {
853
+ const priorityIcon = step.priority === "high" ? "🔴" : step.priority === "medium" ? "🟡" : "🟢";
854
+ responseText += `${idx + 1}. ${priorityIcon} **${step.action}**`;
855
+ if (step.timeframe) {
856
+ responseText += ` *(${step.timeframe})*`;
857
+ }
858
+ if (step.reasoning) {
859
+ responseText += `\n - Reasoning: ${step.reasoning}`;
860
+ }
861
+ responseText += `\n`;
862
+ });
863
+ }
864
+ responseText += `\n`;
865
+ }
866
+ // Key Facts (if available)
867
+ if (synthesis.keyFacts && synthesis.keyFacts.length > 0) {
868
+ responseText += `## 📌 Key Facts\n`;
869
+ synthesis.keyFacts.slice(0, 5).forEach((fact) => {
870
+ if (typeof fact === 'string') {
871
+ responseText += `- ${fact}\n`;
872
+ }
873
+ else if (fact.text) {
874
+ responseText += `- ${fact.text}`;
875
+ if (fact.sourceTitle) {
876
+ responseText += ` *(${fact.sourceTitle})*`;
877
+ }
878
+ responseText += `\n`;
879
+ }
880
+ });
881
+ responseText += `\n`;
882
+ }
883
+ // Source Information
884
+ if (sources && sources.length > 0) {
885
+ responseText += `## 📚 Sources Analyzed\n`;
886
+ sources.forEach((source, idx) => {
887
+ const typeIcons = {
888
+ youtube: "📺",
889
+ x_twitter: "𝕏",
890
+ reddit: "🔗",
891
+ article: "📄",
892
+ pdf: "📑"
893
+ };
894
+ const typeIcon = typeIcons[source.type] || "📄";
895
+ responseText += `${idx + 1}. ${typeIcon} [${source.title}](${source.url})\n`;
896
+ });
897
+ responseText += `\n`;
898
+ }
899
+ // Metadata Footer
900
+ responseText += `---\n`;
901
+ responseText += `*Analysis completed in ${metadata.executionTime ? `${metadata.executionTime}ms` : 'N/A'}`;
902
+ if (metadata.totalTokens) {
903
+ responseText += ` | Processed ${metadata.totalTokens.toLocaleString()} tokens`;
904
+ }
905
+ if (metadata.focusAreas && metadata.focusAreas.length > 0) {
906
+ responseText += ` | Focus: ${metadata.focusAreas.join(', ')}`;
907
+ }
908
+ responseText += `*\n`;
909
+ return {
910
+ content: [
911
+ {
912
+ type: "text",
913
+ text: responseText,
914
+ },
915
+ ],
916
+ data: result,
917
+ };
918
+ },
919
+ },
920
+ {
921
+ name: "find_similar_content",
922
+ description: "Find content similar to a specific saved item using semantic similarity.",
923
+ inputSchema: {
924
+ type: "object",
925
+ properties: {
926
+ contentId: {
927
+ type: "string",
928
+ description: "ID of the content to find similar items for",
929
+ },
930
+ limit: {
931
+ type: "number",
932
+ description: "Maximum number of similar items to return",
933
+ default: 5,
934
+ },
935
+ minSimilarity: {
936
+ type: "number",
937
+ description: "Minimum similarity score (0-1)",
938
+ default: 0.7,
939
+ },
940
+ },
941
+ required: ["contentId"],
942
+ },
943
+ modifies: false,
944
+ handler: async (client, args) => {
945
+ const schema = z.object({
946
+ contentId: z.string(),
947
+ limit: z.number().optional().default(5),
948
+ minSimilarity: z.number().optional().default(0.7),
949
+ });
950
+ const params = schema.parse(args);
951
+ const result = await client.findSimilarContent(params.contentId, {
952
+ limit: params.limit,
953
+ minSimilarity: params.minSimilarity,
954
+ });
955
+ let responseText = `# Similar Content to: ${result.source?.title || "Unknown"}\n\n`;
956
+ if (result.similarContent && result.similarContent.length > 0) {
957
+ responseText += `Found ${result.similarContent.length} similar items:\n\n`;
958
+ result.similarContent.forEach((item, idx) => {
959
+ responseText += `## ${idx + 1}. ${item.title || "Untitled"}\n`;
960
+ responseText += `**Similarity:** ${(item.similarity * 100).toFixed(1)}%\n`;
961
+ responseText += `**Type:** ${item.type || item.contentType || "unknown"} | **URL:** ${item.url}\n`;
962
+ if (item.summary) {
963
+ responseText += `**Summary:** ${item.summary}\n`;
964
+ }
965
+ responseText += `\n`;
966
+ });
967
+ }
968
+ else {
969
+ responseText += `No similar content found with similarity >= ${params.minSimilarity}`;
970
+ }
971
+ return {
972
+ content: [
973
+ {
974
+ type: "text",
975
+ text: responseText,
976
+ },
977
+ ],
978
+ data: result,
979
+ };
980
+ },
981
+ },
982
+ {
983
+ name: "batch_get_content",
984
+ description: "Fetch multiple content items in a single request. Efficient for bulk operations.",
985
+ inputSchema: {
986
+ type: "object",
987
+ properties: {
988
+ ids: {
989
+ type: "array",
990
+ items: {
991
+ type: "string",
992
+ },
993
+ description: "Array of content IDs to fetch",
994
+ },
995
+ includeFullContent: {
996
+ type: "boolean",
997
+ description: "Include full text content (warning: may use many tokens)",
998
+ default: false,
999
+ },
1000
+ },
1001
+ required: ["ids"],
1002
+ },
1003
+ modifies: false,
1004
+ handler: async (client, args) => {
1005
+ const schema = z.object({
1006
+ ids: z.array(z.string()).min(1).max(50),
1007
+ includeFullContent: z.boolean().optional().default(false),
1008
+ });
1009
+ const params = schema.parse(args);
1010
+ const result = await client.batchGetContent(params.ids, params.includeFullContent);
1011
+ let responseText = `# Batch Content Fetch\n\n`;
1012
+ responseText += `Requested: ${params.ids.length} items\n`;
1013
+ responseText += `Found: ${result.metadata?.found || 0} items\n`;
1014
+ // Calculate and warn about total tokens
1015
+ let totalTokens = 0;
1016
+ if (result.results && result.results.length > 0) {
1017
+ result.results.forEach((item) => {
1018
+ if (item.tokenCount)
1019
+ totalTokens += item.tokenCount;
1020
+ });
1021
+ }
1022
+ if (params.includeFullContent && totalTokens > 0) {
1023
+ responseText += `\n⚠️ **Full Content Included - Total Size: ${totalTokens.toLocaleString()} tokens**`;
1024
+ if (totalTokens > 100000) {
1025
+ responseText += ` 🚨 EXTREMELY LARGE!\n`;
1026
+ responseText += `WARNING: This WILL consume most or all of your context window!\n`;
1027
+ responseText += `Consider setting includeFullContent: false to just get summaries.\n`;
1028
+ }
1029
+ else if (totalTokens > 50000) {
1030
+ responseText += ` 🚨 VERY LARGE!\n`;
1031
+ responseText += `This is consuming significant context. Use with caution.\n`;
1032
+ }
1033
+ else if (totalTokens > 10000) {
1034
+ responseText += ` ⚠️\n`;
1035
+ }
1036
+ else {
1037
+ responseText += `\n`;
1038
+ }
1039
+ }
1040
+ else if (!params.includeFullContent && totalTokens > 0) {
1041
+ responseText += `\n💡 **Showing summaries only.** Total content available: ~${totalTokens.toLocaleString()} tokens.\n`;
1042
+ responseText += `Rich metadata (summaries, tags, insights) included with minimal token usage.\n`;
1043
+ }
1044
+ responseText += `\n`;
1045
+ if (result.results && result.results.length > 0) {
1046
+ result.results.forEach((item, idx) => {
1047
+ if (item.error) {
1048
+ responseText += `## ${idx + 1}. Error: ${item.id}\n`;
1049
+ responseText += `${item.error}\n\n`;
1050
+ }
1051
+ else {
1052
+ responseText += `## ${idx + 1}. ${item.title || "Untitled"}\n`;
1053
+ responseText += `**ID:** ${item.id}\n`;
1054
+ responseText += `**Type:** ${item.contentType} | **URL:** ${item.url}\n`;
1055
+ if (item.tokenCount) {
1056
+ responseText += `**Tokens:** ${item.tokenCount.toLocaleString()}`;
1057
+ if (item.tokenCount > 10000)
1058
+ responseText += ` ⚠️`;
1059
+ responseText += `\n`;
1060
+ }
1061
+ if (item.summary) {
1062
+ responseText += `**Summary:** ${typeof item.summary === "string" ? item.summary : item.summary.one_sentence || "N/A"}\n`;
1063
+ }
1064
+ if (params.includeFullContent && item.fullContent) {
1065
+ responseText += `**Content Length:** ${item.metadata?.contentLength || 0} characters\n`;
1066
+ }
1067
+ responseText += `\n`;
1068
+ }
1069
+ });
1070
+ }
1071
+ return {
1072
+ content: [
1073
+ {
1074
+ type: "text",
1075
+ text: responseText,
1076
+ },
1077
+ ],
1078
+ data: result,
1079
+ };
1080
+ },
1081
+ },
1082
+ {
1083
+ name: "get_instructions",
1084
+ description: "Get instructions for optimal MCP usage, including capabilities, best practices, and example workflows",
1085
+ inputSchema: {
1086
+ type: "object",
1087
+ properties: {
1088
+ topic: {
1089
+ type: "string",
1090
+ description: "Specific topic for instructions (optional)",
1091
+ enum: ["search", "synthesis", "retrieval", "general"],
1092
+ },
1093
+ includeExamples: {
1094
+ type: "boolean",
1095
+ description: "Include workflow examples",
1096
+ default: true,
1097
+ },
1098
+ },
1099
+ },
1100
+ modifies: false,
1101
+ handler: async (client, args) => {
1102
+ const schema = z.object({
1103
+ topic: z.enum(["search", "synthesis", "retrieval", "general"]).optional(),
1104
+ includeExamples: z.boolean().optional().default(true),
1105
+ });
1106
+ const params = schema.parse(args);
1107
+ const instructions = generateLLMInstructions();
1108
+ let responseText = `# Noverload MCP Usage Instructions\n\n`;
1109
+ responseText += `## Version: ${instructions.version}\n\n`;
1110
+ if (!params.topic || params.topic === "general") {
1111
+ // Show all capabilities
1112
+ responseText += `## Capabilities Overview\n\n`;
1113
+ for (const [key, capability] of Object.entries(instructions.capabilities)) {
1114
+ responseText += `### ${key.charAt(0).toUpperCase() + key.slice(1)}\n`;
1115
+ responseText += `${capability.description}\n\n`;
1116
+ responseText += `**Strengths:**\n`;
1117
+ capability.strengths.forEach((s) => responseText += `- ${s}\n`);
1118
+ responseText += `\n**When to use:**\n`;
1119
+ capability.whenToUse.forEach((w) => responseText += `- ${w}\n`);
1120
+ responseText += `\n`;
1121
+ }
1122
+ responseText += `## Best Practices\n\n`;
1123
+ for (const [scenario, practice] of Object.entries(instructions.bestPractices)) {
1124
+ responseText += `**${scenario.replace(/([A-Z])/g, ' $1').toLowerCase()}:** ${practice}\n`;
1125
+ }
1126
+ responseText += `\n`;
1127
+ }
1128
+ else {
1129
+ // Show specific capability
1130
+ const capability = instructions.capabilities[params.topic];
1131
+ if (capability) {
1132
+ responseText += `## ${params.topic.charAt(0).toUpperCase() + params.topic.slice(1)} Capability\n\n`;
1133
+ responseText += `${capability.description}\n\n`;
1134
+ responseText += `### Strengths\n`;
1135
+ capability.strengths.forEach(s => responseText += `- ${s}\n`);
1136
+ responseText += `\n### Limitations\n`;
1137
+ capability.limitations.forEach(l => responseText += `- ${l}\n`);
1138
+ responseText += `\n### When to Use\n`;
1139
+ capability.whenToUse.forEach(w => responseText += `- ${w}\n`);
1140
+ }
1141
+ }
1142
+ if (params.includeExamples && instructions.exampleWorkflows.length > 0) {
1143
+ responseText += `\n## Example Workflows\n\n`;
1144
+ instructions.exampleWorkflows.forEach((example, idx) => {
1145
+ responseText += `### ${idx + 1}. ${example.scenario}\n`;
1146
+ responseText += `**Steps:**\n`;
1147
+ example.steps.forEach((step, i) => responseText += `${i + 1}. ${step}\n`);
1148
+ responseText += `**Expected Outcome:** ${example.expectedOutcome}\n\n`;
1149
+ });
1150
+ }
1151
+ responseText += `## Token Management\n\n`;
1152
+ responseText += `- Search results limit: ${instructions.tokenManagement.searchResultsLimit}\n`;
1153
+ responseText += `- Use chunking: ${instructions.tokenManagement.useChunking ? 'Yes' : 'No'}\n`;
1154
+ responseText += `- Get summaries first: ${instructions.tokenManagement.summaryFirst ? 'Yes' : 'No'}\n`;
1155
+ responseText += `- Max content per query: ${instructions.tokenManagement.maxContentPerQuery.toLocaleString()} tokens\n`;
1156
+ return {
1157
+ content: [
1158
+ {
1159
+ type: "text",
1160
+ text: responseText,
1161
+ },
1162
+ ],
1163
+ data: instructions,
1164
+ };
1165
+ },
1166
+ },
1167
+ {
1168
+ name: "plan_query",
1169
+ description: "Plan an optimal query strategy for complex requests, with token estimates",
1170
+ inputSchema: {
1171
+ type: "object",
1172
+ properties: {
1173
+ request: {
1174
+ type: "string",
1175
+ description: "The user's request to plan for",
1176
+ },
1177
+ },
1178
+ required: ["request"],
1179
+ },
1180
+ modifies: false,
1181
+ handler: async (client, args) => {
1182
+ const schema = z.object({
1183
+ request: z.string(),
1184
+ });
1185
+ const { request } = schema.parse(args);
1186
+ const availableTools = [
1187
+ "search_content",
1188
+ "get_content_details",
1189
+ "synthesize_content",
1190
+ "find_similar_content",
1191
+ "list_saved_content"
1192
+ ];
1193
+ const plan = planQueryStrategy(request, availableTools);
1194
+ let responseText = `# Query Plan for: "${request}"\n\n`;
1195
+ responseText += `## Strategy: ${plan.strategy}\n`;
1196
+ responseText += `## Estimated Tokens: ~${plan.estimatedTokens.toLocaleString()}\n\n`;
1197
+ responseText += `## Execution Steps:\n\n`;
1198
+ plan.steps.forEach((step, idx) => {
1199
+ responseText += `### Step ${idx + 1}: ${step.tool}\n`;
1200
+ responseText += `**Purpose:** ${step.purpose}\n`;
1201
+ responseText += `**Parameters:**\n`;
1202
+ for (const [key, value] of Object.entries(step.params)) {
1203
+ responseText += `- ${key}: ${JSON.stringify(value)}\n`;
1204
+ }
1205
+ const tokenEstimate = estimateTokenUsage({ tool: step.tool, params: step.params });
1206
+ responseText += `**Estimated Tokens:** ~${tokenEstimate.estimated.toLocaleString()}`;
1207
+ if (tokenEstimate.warning) {
1208
+ responseText += ` ⚠️ ${tokenEstimate.warning}`;
1209
+ }
1210
+ responseText += `\n`;
1211
+ if (tokenEstimate.suggestion) {
1212
+ responseText += `**Suggestion:** ${tokenEstimate.suggestion}\n`;
1213
+ }
1214
+ responseText += `\n`;
1215
+ });
1216
+ return {
1217
+ content: [
1218
+ {
1219
+ type: "text",
1220
+ text: responseText,
1221
+ },
1222
+ ],
1223
+ data: plan,
1224
+ };
1225
+ },
1226
+ },
1227
+ {
1228
+ name: "get_raw_content",
1229
+ description: "Access full raw text of saved content with smart chunking for large documents. Requires confirmation for content >10k tokens. Returns complete unprocessed text or intelligently chunked sections.",
1230
+ inputSchema: {
1231
+ type: "object",
1232
+ properties: {
1233
+ contentId: {
1234
+ type: "string",
1235
+ description: "The ID of the content to retrieve raw text for",
1236
+ },
1237
+ format: {
1238
+ type: "string",
1239
+ enum: ["full", "chunks", "sections"],
1240
+ description: "Format: 'full' (complete text), 'chunks' (semantic chunks), 'sections' (logical sections)",
1241
+ default: "chunks",
1242
+ },
1243
+ maxTokens: {
1244
+ type: "number",
1245
+ description: "Maximum tokens to return (only for chunks/sections format)",
1246
+ default: 50000,
1247
+ },
1248
+ chunkSize: {
1249
+ type: "number",
1250
+ description: "Target size for each chunk in tokens (for chunks format)",
1251
+ default: 4000,
1252
+ },
1253
+ includeMetadata: {
1254
+ type: "boolean",
1255
+ description: "Include metadata about sections/chunks",
1256
+ default: true,
1257
+ },
1258
+ confirmLargeContent: {
1259
+ type: "boolean",
1260
+ description: "Confirm retrieval of large content (>10k tokens)",
1261
+ default: false,
1262
+ },
1263
+ },
1264
+ required: ["contentId"],
1265
+ },
1266
+ modifies: false,
1267
+ handler: async (client, args) => {
1268
+ const schema = z.object({
1269
+ contentId: z.string(),
1270
+ format: z.enum(["full", "chunks", "sections"]).optional().default("chunks"),
1271
+ maxTokens: z.number().optional().default(50000),
1272
+ chunkSize: z.number().optional().default(4000),
1273
+ includeMetadata: z.boolean().optional().default(true),
1274
+ confirmLargeContent: z.boolean().optional().default(false),
1275
+ });
1276
+ const params = schema.parse(args);
1277
+ // First get content metadata to check size
1278
+ const content = await client.getContent(params.contentId);
1279
+ if (!content.rawText) {
1280
+ // Try to get raw content through v2 API with enrichment
1281
+ const enrichedResult = await client.getEnrichedContent([params.contentId], true);
1282
+ if (!enrichedResult || enrichedResult.length === 0 || !enrichedResult[0].rawText) {
1283
+ return {
1284
+ content: [
1285
+ {
1286
+ type: "text",
1287
+ text: `No raw text available for content ID: ${params.contentId}. Content may still be processing or does not contain extractable text.`,
1288
+ },
1289
+ ],
1290
+ data: null,
1291
+ };
1292
+ }
1293
+ // Use the enriched content
1294
+ content.rawText = enrichedResult[0].rawText;
1295
+ content.tokenCount = enrichedResult[0].tokenCount;
1296
+ }
1297
+ const wordCount = content.rawText ? content.rawText.split(/\s+/).length : 0;
1298
+ const estimatedTokens = content.tokenCount || Math.ceil(wordCount * 1.3);
1299
+ // Check if confirmation is needed for large content
1300
+ if (params.format === "full" && estimatedTokens > 10000 && !params.confirmLargeContent) {
1301
+ let warningText = `# ⚠️ Large Content Warning\n\n`;
1302
+ warningText += `**Content:** ${content.title || 'Untitled'}\n`;
1303
+ warningText += `**Size:** ${wordCount.toLocaleString()} words (~${estimatedTokens.toLocaleString()} tokens)\n\n`;
1304
+ if (estimatedTokens > 100000) {
1305
+ warningText += `## 🚨 CRITICAL: Extremely Large Content\n`;
1306
+ warningText += `This content contains ${estimatedTokens.toLocaleString()} tokens and will **exceed most LLM context windows**.\n\n`;
1307
+ warningText += `**Recommendations:**\n`;
1308
+ warningText += `1. Use format='chunks' to process in smaller pieces\n`;
1309
+ warningText += `2. Use format='sections' to get logical divisions\n`;
1310
+ warningText += `3. Use search_content to find specific information\n`;
1311
+ warningText += `4. Use synthesize_content to get key insights without full text\n\n`;
1312
+ }
1313
+ else if (estimatedTokens > 50000) {
1314
+ warningText += `## ⚠️ Very Large Content\n`;
1315
+ warningText += `This content contains ${estimatedTokens.toLocaleString()} tokens and will use **most of your context window**.\n\n`;
1316
+ warningText += `**Consider:**\n`;
1317
+ warningText += `- Using format='chunks' for manageable pieces\n`;
1318
+ warningText += `- Using search or synthesis tools instead\n\n`;
1319
+ }
1320
+ else {
1321
+ warningText += `## Large Content Notice\n`;
1322
+ warningText += `This content will use ${estimatedTokens.toLocaleString()} tokens of your context.\n\n`;
1323
+ }
1324
+ warningText += `**To proceed with full text retrieval:**\n`;
1325
+ warningText += `Call get_raw_content again with:\n`;
1326
+ warningText += `- contentId: "${params.contentId}"\n`;
1327
+ warningText += `- format: "full"\n`;
1328
+ warningText += `- confirmLargeContent: true\n\n`;
1329
+ warningText += `**Alternative approaches:**\n`;
1330
+ warningText += `- format: "chunks" - Get content in ${Math.ceil(estimatedTokens / params.chunkSize)} chunks of ~${params.chunkSize} tokens\n`;
1331
+ warningText += `- format: "sections" - Get logical sections with headers\n`;
1332
+ return {
1333
+ content: [
1334
+ {
1335
+ type: "text",
1336
+ text: warningText,
1337
+ },
1338
+ ],
1339
+ data: {
1340
+ requiresConfirmation: true,
1341
+ contentId: params.contentId,
1342
+ title: content.title,
1343
+ estimatedTokens,
1344
+ suggestedFormat: estimatedTokens > 50000 ? "chunks" : "sections",
1345
+ chunkCount: Math.ceil(estimatedTokens / params.chunkSize),
1346
+ },
1347
+ };
1348
+ }
1349
+ let responseText = `# Raw Content: ${content.title || 'Untitled'}\n\n`;
1350
+ responseText += `**Content Type:** ${content.contentType}\n`;
1351
+ responseText += `**URL:** ${content.url}\n`;
1352
+ responseText += `**Size:** ${wordCount.toLocaleString()} words (~${estimatedTokens.toLocaleString()} tokens)\n`;
1353
+ // Add appropriate warning based on size
1354
+ if (estimatedTokens > 50000 && params.format === "full") {
1355
+ responseText += `\n⚠️ **Large content confirmed** - Returning ${estimatedTokens.toLocaleString()} tokens\n\n`;
1356
+ }
1357
+ let resultData = {
1358
+ id: content.id,
1359
+ title: content.title,
1360
+ contentType: content.contentType,
1361
+ totalTokens: estimatedTokens,
1362
+ format: params.format,
1363
+ };
1364
+ if (params.format === "full") {
1365
+ // Return full text (with confirmation)
1366
+ responseText += `\n## Full Text Content\n`;
1367
+ responseText += `*Complete raw text (${estimatedTokens.toLocaleString()} tokens) is available in the data field.*\n`;
1368
+ resultData.text = content.rawText;
1369
+ resultData.metadata = params.includeMetadata ? {
1370
+ wordCount,
1371
+ estimatedTokens,
1372
+ contentType: content.contentType,
1373
+ extractedAt: content.createdAt,
1374
+ } : undefined;
1375
+ }
1376
+ else if (params.format === "chunks") {
1377
+ // Smart chunking based on token size
1378
+ const chunks = chunkContent(content.rawText || "", params.chunkSize, params.maxTokens);
1379
+ responseText += `\n## Content Chunks\n`;
1380
+ responseText += `**Total Chunks:** ${chunks.length}\n`;
1381
+ responseText += `**Chunk Size:** ~${params.chunkSize} tokens each\n`;
1382
+ if (chunks.length * params.chunkSize > params.maxTokens) {
1383
+ responseText += `**Note:** Showing first ${chunks.length} chunks (limited by maxTokens: ${params.maxTokens})\n`;
1384
+ }
1385
+ responseText += `\n`;
1386
+ // Show chunk previews
1387
+ chunks.slice(0, 3).forEach((chunk, idx) => {
1388
+ responseText += `### Chunk ${idx + 1}/${chunks.length}\n`;
1389
+ responseText += `**Tokens:** ~${chunk.tokenCount}\n`;
1390
+ responseText += `**Preview:** ${chunk.text.slice(0, 200)}...\n\n`;
1391
+ });
1392
+ if (chunks.length > 3) {
1393
+ responseText += `*... and ${chunks.length - 3} more chunks*\n\n`;
1394
+ }
1395
+ responseText += `💡 **Tip:** To get a specific chunk, note the chunk index and request it directly.\n`;
1396
+ resultData.chunks = chunks;
1397
+ resultData.totalChunks = chunks.length;
1398
+ }
1399
+ else if (params.format === "sections") {
1400
+ // Logical section detection (headers, paragraphs, etc.)
1401
+ const sections = detectSections(content.rawText || "", content.contentType);
1402
+ responseText += `\n## Content Sections\n`;
1403
+ responseText += `**Total Sections:** ${sections.length}\n\n`;
1404
+ // Show section outline
1405
+ let totalSectionTokens = 0;
1406
+ sections.forEach((section, idx) => {
1407
+ responseText += `${idx + 1}. **${section.title || `Section ${idx + 1}`}**`;
1408
+ if (section.type) {
1409
+ responseText += ` (${section.type})`;
1410
+ }
1411
+ responseText += ` - ~${section.tokenCount} tokens\n`;
1412
+ totalSectionTokens += section.tokenCount;
1413
+ if (totalSectionTokens > params.maxTokens) {
1414
+ responseText += ` *[Exceeds maxTokens limit]*\n`;
1415
+ }
1416
+ });
1417
+ responseText += `\n*Full section content available in data field*\n`;
1418
+ resultData.sections = sections;
1419
+ resultData.totalSections = sections.length;
1420
+ }
1421
+ return {
1422
+ content: [
1423
+ {
1424
+ type: "text",
1425
+ text: responseText,
1426
+ },
1427
+ ],
1428
+ data: resultData,
1429
+ };
1430
+ },
1431
+ },
1432
+ {
1433
+ name: "explore_topic",
1434
+ description: "Deep exploration of a topic across all saved content. Provides comprehensive understanding with multiple perspectives, evolution over time, and key concepts.",
1435
+ inputSchema: {
1436
+ type: "object",
1437
+ properties: {
1438
+ topic: {
1439
+ type: "string",
1440
+ description: "The topic to explore comprehensively",
1441
+ },
1442
+ depth: {
1443
+ type: "string",
1444
+ enum: ["surface", "comprehensive", "expert"],
1445
+ description: "Depth of exploration",
1446
+ default: "comprehensive",
1447
+ },
1448
+ includeTimeline: {
1449
+ type: "boolean",
1450
+ description: "Include chronological evolution of the topic",
1451
+ default: true,
1452
+ },
1453
+ includeConnections: {
1454
+ type: "boolean",
1455
+ description: "Find connections to related topics",
1456
+ default: true,
1457
+ },
1458
+ maxSources: {
1459
+ type: "number",
1460
+ description: "Maximum number of sources to analyze",
1461
+ default: 20,
1462
+ },
1463
+ },
1464
+ required: ["topic"],
1465
+ },
1466
+ modifies: false,
1467
+ handler: async (client, args) => {
1468
+ const schema = z.object({
1469
+ topic: z.string(),
1470
+ depth: z.enum(["surface", "comprehensive", "expert"]).optional().default("comprehensive"),
1471
+ includeTimeline: z.boolean().optional().default(true),
1472
+ includeConnections: z.boolean().optional().default(true),
1473
+ maxSources: z.number().optional().default(20),
1474
+ });
1475
+ const params = schema.parse(args);
1476
+ // Search for all content related to the topic
1477
+ const searchResults = await client.searchContent(params.topic, {
1478
+ limit: params.maxSources,
1479
+ enableConceptExpansion: true,
1480
+ fuzzyMatch: true,
1481
+ });
1482
+ if (!searchResults || searchResults.length === 0) {
1483
+ return {
1484
+ content: [
1485
+ {
1486
+ type: "text",
1487
+ text: `No content found for topic: "${params.topic}". Try saving relevant content first or use a different search term.`,
1488
+ },
1489
+ ],
1490
+ data: null,
1491
+ };
1492
+ }
1493
+ // Synthesize the content for deep understanding
1494
+ const synthesis = await client.synthesizeContent({
1495
+ query: `Comprehensive exploration of ${params.topic}`,
1496
+ contentIds: searchResults.slice(0, params.maxSources).map((r) => r.id),
1497
+ synthesisMode: params.depth === "expert" ? "deep" : params.depth === "surface" ? "overview" : "actionable",
1498
+ findConnections: params.includeConnections,
1499
+ findContradictions: true,
1500
+ });
1501
+ let responseText = `# 🔍 Topic Exploration: "${params.topic}"\n`;
1502
+ responseText += `**Depth:** ${params.depth} | **Sources Analyzed:** ${searchResults.length}\n\n`;
1503
+ // Overview Section
1504
+ responseText += `## 📋 Overview\n`;
1505
+ if (synthesis.synthesis?.summary) {
1506
+ responseText += `${synthesis.synthesis.summary}\n\n`;
1507
+ }
1508
+ else {
1509
+ responseText += `Analysis of ${searchResults.length} pieces of content about ${params.topic}.\n\n`;
1510
+ }
1511
+ // Key Concepts
1512
+ responseText += `## 🎯 Key Concepts\n`;
1513
+ const concepts = extractKeyConceptsFromSearch(searchResults, params.topic);
1514
+ concepts.slice(0, 10).forEach((concept, idx) => {
1515
+ responseText += `${idx + 1}. ${concept}\n`;
1516
+ });
1517
+ responseText += `\n`;
1518
+ // Multiple Perspectives
1519
+ responseText += `## 👥 Different Perspectives\n`;
1520
+ const perspectives = groupByPerspective(searchResults);
1521
+ Object.entries(perspectives).slice(0, 5).forEach(([perspective, items]) => {
1522
+ responseText += `\n### ${perspective}\n`;
1523
+ responseText += `*Found in ${items.length} sources*\n`;
1524
+ if (items[0]?.summary) {
1525
+ const summary = typeof items[0].summary === 'string' ? items[0].summary : items[0].summary.one_sentence;
1526
+ responseText += `Example: "${summary?.slice(0, 200)}..."\n`;
1527
+ }
1528
+ });
1529
+ responseText += `\n`;
1530
+ // Timeline (if requested)
1531
+ if (params.includeTimeline && searchResults.length > 1) {
1532
+ responseText += `## ⏱️ Topic Evolution\n`;
1533
+ const timeline = createTopicTimeline(searchResults);
1534
+ timeline.forEach((event) => {
1535
+ responseText += `- **${event.date}:** ${event.title} - ${event.insight}\n`;
1536
+ });
1537
+ responseText += `\n`;
1538
+ }
1539
+ // Connections (if found)
1540
+ if (params.includeConnections && synthesis.synthesis?.connections) {
1541
+ responseText += `## 🔗 Related Topics & Connections\n`;
1542
+ synthesis.synthesis.connections.slice(0, 8).forEach((conn) => {
1543
+ if (typeof conn === 'string') {
1544
+ responseText += `- ${conn}\n`;
1545
+ }
1546
+ else if (conn.concept) {
1547
+ responseText += `- **${conn.concept}**`;
1548
+ if (conn.strength) {
1549
+ responseText += ` (strength: ${conn.strength})`;
1550
+ }
1551
+ responseText += `\n`;
1552
+ }
1553
+ });
1554
+ responseText += `\n`;
1555
+ }
1556
+ // Contradictions & Debates
1557
+ if (synthesis.synthesis?.contradictions && synthesis.synthesis.contradictions.length > 0) {
1558
+ responseText += `## ⚡ Contradictions & Debates\n`;
1559
+ synthesis.synthesis.contradictions.slice(0, 3).forEach((contra) => {
1560
+ responseText += `\n**Point of Contention:**\n`;
1561
+ if (contra.topic) {
1562
+ responseText += `${contra.topic}\n`;
1563
+ }
1564
+ if (contra.viewpoints) {
1565
+ contra.viewpoints.forEach((vp) => {
1566
+ responseText += `- ${vp.position}: ${vp.argument}\n`;
1567
+ });
1568
+ }
1569
+ });
1570
+ responseText += `\n`;
1571
+ }
1572
+ // Learning Path (for comprehensive/expert depth)
1573
+ if (params.depth !== "surface") {
1574
+ responseText += `## 📚 Suggested Learning Path\n`;
1575
+ const learningPath = createLearningPath(searchResults, params.topic);
1576
+ learningPath.forEach((step, idx) => {
1577
+ responseText += `${idx + 1}. **${step.title}** - ${step.reason}\n`;
1578
+ });
1579
+ responseText += `\n`;
1580
+ }
1581
+ // Expert Insights (for expert depth)
1582
+ if (params.depth === "expert" && synthesis.synthesis?.insights) {
1583
+ responseText += `## 💡 Expert-Level Insights\n`;
1584
+ synthesis.synthesis.insights.slice(0, 5).forEach((insight, idx) => {
1585
+ const text = typeof insight === 'string' ? insight : insight.text;
1586
+ responseText += `${idx + 1}. ${text}\n`;
1587
+ });
1588
+ responseText += `\n`;
1589
+ }
1590
+ // Sources
1591
+ responseText += `## 📚 Top Sources\n`;
1592
+ searchResults.slice(0, 5).forEach((source, idx) => {
1593
+ const typeIcons = {
1594
+ youtube: "📺",
1595
+ x_twitter: "𝕏",
1596
+ reddit: "🔗",
1597
+ article: "📄",
1598
+ pdf: "📑"
1599
+ };
1600
+ const icon = typeIcons[source.contentType] || "📄";
1601
+ responseText += `${idx + 1}. ${icon} [${source.title || "Untitled"}](${source.url})\n`;
1602
+ });
1603
+ return {
1604
+ content: [
1605
+ {
1606
+ type: "text",
1607
+ text: responseText,
1608
+ },
1609
+ ],
1610
+ data: {
1611
+ topic: params.topic,
1612
+ depth: params.depth,
1613
+ sourcesAnalyzed: searchResults.length,
1614
+ synthesis: synthesis.synthesis,
1615
+ sources: searchResults,
1616
+ concepts,
1617
+ perspectives,
1618
+ },
1619
+ };
1620
+ },
1621
+ },
1622
+ {
1623
+ name: "find_connections",
1624
+ description: "Discover connections and relationships between different pieces of content. Identifies causal links, contradictions, and complementary information.",
1625
+ inputSchema: {
1626
+ type: "object",
1627
+ properties: {
1628
+ contentIds: {
1629
+ type: "array",
1630
+ items: {
1631
+ type: "string",
1632
+ },
1633
+ description: "Array of content IDs to find connections between",
1634
+ },
1635
+ connectionType: {
1636
+ type: "string",
1637
+ enum: ["all", "causal", "contradictory", "complementary", "sequential"],
1638
+ description: "Type of connections to find",
1639
+ default: "all",
1640
+ },
1641
+ depth: {
1642
+ type: "number",
1643
+ description: "How many levels deep to search for connections (1-3)",
1644
+ default: 1,
1645
+ },
1646
+ },
1647
+ required: ["contentIds"],
1648
+ },
1649
+ modifies: false,
1650
+ handler: async (client, args) => {
1651
+ const schema = z.object({
1652
+ contentIds: z.array(z.string()).min(2),
1653
+ connectionType: z.enum(["all", "causal", "contradictory", "complementary", "sequential"]).optional().default("all"),
1654
+ depth: z.number().min(1).max(3).optional().default(1),
1655
+ });
1656
+ const params = schema.parse(args);
1657
+ // Fetch all content items
1658
+ const contents = await client.batchGetContent(params.contentIds, false);
1659
+ if (!contents.results || contents.results.length < 2) {
1660
+ return {
1661
+ content: [
1662
+ {
1663
+ type: "text",
1664
+ text: "Unable to find connections. Need at least 2 valid content items.",
1665
+ },
1666
+ ],
1667
+ data: null,
1668
+ };
1669
+ }
1670
+ // Analyze connections between content
1671
+ const connections = analyzeConnections(contents.results, params.connectionType);
1672
+ let responseText = `# 🔗 Content Connections Analysis\n`;
1673
+ responseText += `**Analyzing:** ${contents.results.length} pieces of content\n`;
1674
+ responseText += `**Connection Type:** ${params.connectionType}\n`;
1675
+ responseText += `**Connections Found:** ${connections.length}\n\n`;
1676
+ if (connections.length === 0) {
1677
+ responseText += `No ${params.connectionType} connections found between the provided content.\n`;
1678
+ responseText += `Try using connectionType='all' for broader analysis.\n`;
1679
+ }
1680
+ else {
1681
+ // Group connections by type
1682
+ const byType = groupConnectionsByType(connections);
1683
+ Object.entries(byType).forEach(([type, conns]) => {
1684
+ responseText += `## ${getConnectionTypeEmoji(type)} ${type} Connections (${conns.length})\n\n`;
1685
+ conns.slice(0, 5).forEach((conn) => {
1686
+ responseText += `### ${conn.source1.title} ↔️ ${conn.source2.title}\n`;
1687
+ responseText += `**Relationship:** ${conn.relationship}\n`;
1688
+ responseText += `**Strength:** ${getStrengthIndicator(conn.strength)}\n`;
1689
+ if (conn.explanation) {
1690
+ responseText += `**Why:** ${conn.explanation}\n`;
1691
+ }
1692
+ if (conn.sharedConcepts && conn.sharedConcepts.length > 0) {
1693
+ responseText += `**Shared Concepts:** ${conn.sharedConcepts.join(', ')}\n`;
1694
+ }
1695
+ responseText += `\n`;
1696
+ });
1697
+ });
1698
+ // Network summary
1699
+ responseText += `## 🌐 Connection Network Summary\n`;
1700
+ const networkStats = calculateNetworkStats(connections, contents.results);
1701
+ responseText += `- **Most Connected:** ${networkStats.mostConnected.title} (${networkStats.mostConnected.connectionCount} connections)\n`;
1702
+ responseText += `- **Central Theme:** ${networkStats.centralTheme}\n`;
1703
+ responseText += `- **Network Density:** ${networkStats.density}\n`;
1704
+ if (params.depth > 1) {
1705
+ responseText += `\n## 🔍 Extended Connections (Depth ${params.depth})\n`;
1706
+ responseText += `*Searching for indirect connections through related content...*\n`;
1707
+ // This would require additional searches for related content
1708
+ responseText += `Use find_similar_content on highly connected items to explore further.\n`;
1709
+ }
1710
+ }
1711
+ return {
1712
+ content: [
1713
+ {
1714
+ type: "text",
1715
+ text: responseText,
1716
+ },
1717
+ ],
1718
+ data: {
1719
+ connections,
1720
+ contents: contents.results,
1721
+ networkStats: connections.length > 0 ? calculateNetworkStats(connections, contents.results) : null,
1722
+ },
1723
+ };
1724
+ },
1725
+ },
1726
+ {
1727
+ name: "extract_insights",
1728
+ description: "Extract specific types of insights across multiple content sources. Finds patterns, contradictions, consensus, or evolution of ideas.",
1729
+ inputSchema: {
1730
+ type: "object",
1731
+ properties: {
1732
+ query: {
1733
+ type: "string",
1734
+ description: "What insights to extract (e.g., 'productivity tips', 'AI risks', 'investment strategies')",
1735
+ },
1736
+ insightType: {
1737
+ type: "string",
1738
+ enum: ["patterns", "contradictions", "consensus", "evolution", "actionable", "warnings"],
1739
+ description: "Type of insights to extract",
1740
+ default: "patterns",
1741
+ },
1742
+ maxSources: {
1743
+ type: "number",
1744
+ description: "Maximum number of sources to analyze",
1745
+ default: 15,
1746
+ },
1747
+ minConfidence: {
1748
+ type: "number",
1749
+ description: "Minimum confidence score for insights (0-1)",
1750
+ default: 0.7,
1751
+ },
1752
+ },
1753
+ required: ["query"],
1754
+ },
1755
+ modifies: false,
1756
+ handler: async (client, args) => {
1757
+ const schema = z.object({
1758
+ query: z.string(),
1759
+ insightType: z.enum(["patterns", "contradictions", "consensus", "evolution", "actionable", "warnings"]).optional().default("patterns"),
1760
+ maxSources: z.number().optional().default(15),
1761
+ minConfidence: z.number().min(0).max(1).optional().default(0.7),
1762
+ });
1763
+ const params = schema.parse(args);
1764
+ // Search for relevant content
1765
+ const searchResults = await client.searchContent(params.query, {
1766
+ limit: params.maxSources,
1767
+ enableConceptExpansion: true,
1768
+ });
1769
+ if (!searchResults || searchResults.length === 0) {
1770
+ return {
1771
+ content: [
1772
+ {
1773
+ type: "text",
1774
+ text: `No content found for extracting insights about: "${params.query}"`,
1775
+ },
1776
+ ],
1777
+ data: null,
1778
+ };
1779
+ }
1780
+ // Extract insights based on type
1781
+ const insights = extractTypedInsights(searchResults, params.query, params.insightType, params.minConfidence);
1782
+ let responseText = `# 💡 Insight Extraction: "${params.query}"\n`;
1783
+ responseText += `**Type:** ${params.insightType} | **Sources:** ${searchResults.length} | **Min Confidence:** ${(params.minConfidence * 100).toFixed(0)}%\n\n`;
1784
+ if (insights.length === 0) {
1785
+ responseText += `No ${params.insightType} insights found with confidence >= ${params.minConfidence}.\n`;
1786
+ responseText += `Try lowering the confidence threshold or using a different insight type.\n`;
1787
+ }
1788
+ else {
1789
+ // Display insights by type
1790
+ switch (params.insightType) {
1791
+ case "patterns":
1792
+ responseText += `## 🔄 Recurring Patterns\n\n`;
1793
+ insights.forEach((insight, idx) => {
1794
+ responseText += `### Pattern ${idx + 1}: ${insight.pattern}\n`;
1795
+ responseText += `**Frequency:** Appears in ${insight.frequency}/${searchResults.length} sources\n`;
1796
+ responseText += `**Confidence:** ${(insight.confidence * 100).toFixed(0)}%\n`;
1797
+ responseText += `**Examples:**\n`;
1798
+ insight.examples.slice(0, 3).forEach((ex) => {
1799
+ responseText += `- "${ex.text}" *(${ex.source})*\n`;
1800
+ });
1801
+ responseText += `\n`;
1802
+ });
1803
+ break;
1804
+ case "contradictions":
1805
+ responseText += `## ⚡ Contradictions & Disagreements\n\n`;
1806
+ insights.forEach((insight, idx) => {
1807
+ responseText += `### Contradiction ${idx + 1}: ${insight.topic}\n`;
1808
+ responseText += `**Position A:** "${insight.position1.statement}"\n`;
1809
+ responseText += `*Sources:* ${insight.position1.sources.join(', ')}\n\n`;
1810
+ responseText += `**Position B:** "${insight.position2.statement}"\n`;
1811
+ responseText += `*Sources:* ${insight.position2.sources.join(', ')}\n`;
1812
+ if (insight.resolution) {
1813
+ responseText += `\n**Possible Resolution:** ${insight.resolution}\n`;
1814
+ }
1815
+ responseText += `\n`;
1816
+ });
1817
+ break;
1818
+ case "consensus":
1819
+ responseText += `## ✅ Points of Consensus\n\n`;
1820
+ insights.forEach((insight, idx) => {
1821
+ responseText += `${idx + 1}. **${insight.point}**\n`;
1822
+ responseText += ` - Agreement: ${insight.agreementLevel}% of sources\n`;
1823
+ responseText += ` - Confidence: ${(insight.confidence * 100).toFixed(0)}%\n`;
1824
+ if (insight.nuances && insight.nuances.length > 0) {
1825
+ responseText += ` - Nuances: ${insight.nuances.join('; ')}\n`;
1826
+ }
1827
+ });
1828
+ responseText += `\n`;
1829
+ break;
1830
+ case "evolution":
1831
+ responseText += `## 📈 Evolution of Ideas\n\n`;
1832
+ insights.forEach((insight, idx) => {
1833
+ responseText += `### ${insight.concept}\n`;
1834
+ responseText += `**Timeline:**\n`;
1835
+ insight.timeline.forEach((point) => {
1836
+ responseText += `- **${point.date}:** ${point.understanding}\n`;
1837
+ });
1838
+ responseText += `**Current State:** ${insight.currentState}\n`;
1839
+ responseText += `**Trend:** ${insight.trend}\n\n`;
1840
+ });
1841
+ break;
1842
+ case "actionable":
1843
+ responseText += `## 🎯 Actionable Insights\n\n`;
1844
+ insights.forEach((insight, idx) => {
1845
+ responseText += `### ${idx + 1}. ${insight.action}\n`;
1846
+ responseText += `**Why:** ${insight.reasoning}\n`;
1847
+ responseText += `**Priority:** ${insight.priority}\n`;
1848
+ responseText += `**Evidence from:** ${insight.sources.length} sources\n`;
1849
+ if (insight.steps && insight.steps.length > 0) {
1850
+ responseText += `**Steps:**\n`;
1851
+ insight.steps.forEach((step, i) => {
1852
+ responseText += `${i + 1}. ${step}\n`;
1853
+ });
1854
+ }
1855
+ responseText += `\n`;
1856
+ });
1857
+ break;
1858
+ case "warnings":
1859
+ responseText += `## ⚠️ Warnings & Risks\n\n`;
1860
+ insights.forEach((insight, idx) => {
1861
+ const severityEmoji = insight.severity === "high" ? "🔴" : insight.severity === "medium" ? "🟡" : "🟢";
1862
+ responseText += `### ${severityEmoji} ${insight.warning}\n`;
1863
+ responseText += `**Severity:** ${insight.severity}\n`;
1864
+ responseText += `**Mentioned in:** ${insight.sources.length} sources\n`;
1865
+ responseText += `**Context:** ${insight.context}\n`;
1866
+ if (insight.mitigation) {
1867
+ responseText += `**Mitigation:** ${insight.mitigation}\n`;
1868
+ }
1869
+ responseText += `\n`;
1870
+ });
1871
+ break;
1872
+ }
1873
+ // Summary statistics
1874
+ responseText += `## 📊 Extraction Summary\n`;
1875
+ responseText += `- **Total Insights:** ${insights.length}\n`;
1876
+ responseText += `- **Average Confidence:** ${(insights.reduce((sum, i) => sum + i.confidence, 0) / insights.length * 100).toFixed(0)}%\n`;
1877
+ responseText += `- **Sources Analyzed:** ${searchResults.length}\n`;
1878
+ const contributingSources = new Set(insights.flatMap((i) => i.sources || []));
1879
+ responseText += `- **Contributing Sources:** ${contributingSources.size}\n`;
1880
+ }
1881
+ return {
1882
+ content: [
1883
+ {
1884
+ type: "text",
1885
+ text: responseText,
1886
+ },
1887
+ ],
1888
+ data: {
1889
+ query: params.query,
1890
+ insightType: params.insightType,
1891
+ insights,
1892
+ sources: searchResults,
1893
+ },
1894
+ };
1895
+ },
1896
+ },
1897
+ ];
1898
+ // Helper functions for the new tools
1899
+ function chunkContent(text, targetTokenSize, maxTokens) {
1900
+ const words = text.split(/\s+/);
1901
+ const wordsPerChunk = Math.floor(targetTokenSize / 1.3); // Rough token estimation
1902
+ const chunks = [];
1903
+ let totalTokens = 0;
1904
+ for (let i = 0; i < words.length; i += wordsPerChunk) {
1905
+ const chunkWords = words.slice(i, i + wordsPerChunk);
1906
+ const chunkText = chunkWords.join(' ');
1907
+ const chunkTokens = Math.ceil(chunkWords.length * 1.3);
1908
+ // Stop if we've reached the max token limit
1909
+ if (maxTokens && totalTokens + chunkTokens > maxTokens) {
1910
+ break;
1911
+ }
1912
+ chunks.push({
1913
+ index: chunks.length,
1914
+ text: chunkText,
1915
+ tokenCount: chunkTokens,
1916
+ startOffset: i,
1917
+ endOffset: Math.min(i + wordsPerChunk, words.length),
1918
+ });
1919
+ totalTokens += chunkTokens;
1920
+ }
1921
+ return chunks;
1922
+ }
1923
+ function detectSections(text, contentType) {
1924
+ const sections = [];
1925
+ // Simple section detection based on headers and paragraphs
1926
+ const lines = text.split('\n');
1927
+ let currentSection = { title: "Introduction", text: "", type: "intro", tokenCount: 0 };
1928
+ for (const line of lines) {
1929
+ // Detect headers (various formats)
1930
+ if (line.match(/^#{1,6}\s+/) || line.match(/^[A-Z][^.!?]*:$/) || (line.length < 100 && line.match(/^[A-Z]/))) {
1931
+ if (currentSection.text) {
1932
+ currentSection.tokenCount = Math.ceil(currentSection.text.split(/\s+/).length * 1.3);
1933
+ sections.push(currentSection);
1934
+ }
1935
+ currentSection = {
1936
+ title: line.replace(/^#+\s+/, '').replace(/:$/, ''),
1937
+ text: "",
1938
+ type: "section",
1939
+ tokenCount: 0,
1940
+ };
1941
+ }
1942
+ else {
1943
+ currentSection.text += line + "\n";
1944
+ }
1945
+ }
1946
+ // Add the last section
1947
+ if (currentSection.text) {
1948
+ currentSection.tokenCount = Math.ceil(currentSection.text.split(/\s+/).length * 1.3);
1949
+ sections.push(currentSection);
1950
+ }
1951
+ return sections;
1952
+ }
1953
+ function extractKeyConceptsFromSearch(results, topic) {
1954
+ const concepts = new Map();
1955
+ results.forEach(result => {
1956
+ // Extract from tags
1957
+ if (result.tags) {
1958
+ result.tags.forEach((tag) => {
1959
+ concepts.set(tag, (concepts.get(tag) || 0) + 2);
1960
+ });
1961
+ }
1962
+ // Extract from summary
1963
+ if (result.summary) {
1964
+ const summary = typeof result.summary === 'string' ? result.summary : result.summary.one_sentence;
1965
+ if (summary) {
1966
+ // Simple concept extraction from summary
1967
+ const words = summary.toLowerCase().split(/\s+/);
1968
+ const importantWords = words.filter((w) => w.length > 5 && !['about', 'through', 'between', 'during'].includes(w));
1969
+ importantWords.forEach((word) => {
1970
+ concepts.set(word, (concepts.get(word) || 0) + 1);
1971
+ });
1972
+ }
1973
+ }
1974
+ });
1975
+ // Sort by frequency and return top concepts
1976
+ return Array.from(concepts.entries())
1977
+ .sort((a, b) => b[1] - a[1])
1978
+ .map(([concept]) => concept)
1979
+ .filter(concept => concept.toLowerCase() !== topic.toLowerCase());
1980
+ }
1981
+ function groupByPerspective(results) {
1982
+ const perspectives = {};
1983
+ results.forEach(result => {
1984
+ // Group by content type as a simple perspective grouping
1985
+ const perspective = result.contentType || 'general';
1986
+ if (!perspectives[perspective]) {
1987
+ perspectives[perspective] = [];
1988
+ }
1989
+ perspectives[perspective].push(result);
1990
+ });
1991
+ return perspectives;
1992
+ }
1993
+ function createTopicTimeline(results) {
1994
+ // Sort by date and create timeline
1995
+ const sorted = results
1996
+ .filter(r => r.createdAt)
1997
+ .sort((a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime());
1998
+ return sorted.slice(0, 5).map(result => ({
1999
+ date: new Date(result.createdAt).toLocaleDateString(),
2000
+ title: result.title || "Untitled",
2001
+ insight: typeof result.summary === 'string'
2002
+ ? result.summary.slice(0, 100)
2003
+ : result.summary?.one_sentence?.slice(0, 100) || "No summary",
2004
+ }));
2005
+ }
2006
+ function createLearningPath(results, topic) {
2007
+ // Create a simple learning path based on content
2008
+ const path = [];
2009
+ // Find introductory content
2010
+ const intro = results.find(r => r.title?.toLowerCase().includes('introduction') ||
2011
+ r.title?.toLowerCase().includes('basics') ||
2012
+ r.title?.toLowerCase().includes('beginner'));
2013
+ if (intro) {
2014
+ path.push({
2015
+ title: intro.title || "Introduction",
2016
+ reason: "Start with foundational concepts",
2017
+ id: intro.id,
2018
+ });
2019
+ }
2020
+ // Add the most relevant content
2021
+ results
2022
+ .filter(r => r.id !== intro?.id)
2023
+ .slice(0, 3)
2024
+ .forEach(result => {
2025
+ path.push({
2026
+ title: result.title || "Untitled",
2027
+ reason: "Build on core concepts",
2028
+ id: result.id,
2029
+ });
2030
+ });
2031
+ return path;
2032
+ }
2033
+ function analyzeConnections(contents, connectionType) {
2034
+ const connections = [];
2035
+ // Analyze pairs of content
2036
+ for (let i = 0; i < contents.length; i++) {
2037
+ for (let j = i + 1; j < contents.length; j++) {
2038
+ const conn = findConnection(contents[i], contents[j], connectionType);
2039
+ if (conn) {
2040
+ connections.push(conn);
2041
+ }
2042
+ }
2043
+ }
2044
+ return connections;
2045
+ }
2046
+ function findConnection(content1, content2, type) {
2047
+ // Simple connection detection based on shared concepts
2048
+ const tags1 = new Set(content1.tags || []);
2049
+ const tags2 = new Set(content2.tags || []);
2050
+ const sharedTags = Array.from(tags1).filter(t => tags2.has(t));
2051
+ if (sharedTags.length === 0 && type !== "contradictory") {
2052
+ return null;
2053
+ }
2054
+ return {
2055
+ source1: { id: content1.id, title: content1.title || "Untitled" },
2056
+ source2: { id: content2.id, title: content2.title || "Untitled" },
2057
+ type: sharedTags.length > 2 ? "strong" : "weak",
2058
+ relationship: sharedTags.length > 0 ? "complementary" : "independent",
2059
+ strength: sharedTags.length / Math.max(tags1.size, tags2.size),
2060
+ sharedConcepts: sharedTags,
2061
+ explanation: `Share ${sharedTags.length} common concepts`,
2062
+ };
2063
+ }
2064
+ function groupConnectionsByType(connections) {
2065
+ const grouped = {};
2066
+ connections.forEach(conn => {
2067
+ const type = conn.relationship || "unknown";
2068
+ if (!grouped[type]) {
2069
+ grouped[type] = [];
2070
+ }
2071
+ grouped[type].push(conn);
2072
+ });
2073
+ return grouped;
2074
+ }
2075
+ function getConnectionTypeEmoji(type) {
2076
+ const emojis = {
2077
+ complementary: "🤝",
2078
+ contradictory: "⚡",
2079
+ causal: "➡️",
2080
+ sequential: "📅",
2081
+ independent: "🔀",
2082
+ };
2083
+ return emojis[type] || "🔗";
2084
+ }
2085
+ function getStrengthIndicator(strength) {
2086
+ if (strength > 0.8)
2087
+ return "🟢🟢🟢 Very Strong";
2088
+ if (strength > 0.6)
2089
+ return "🟢🟢 Strong";
2090
+ if (strength > 0.4)
2091
+ return "🟡 Moderate";
2092
+ if (strength > 0.2)
2093
+ return "🟠 Weak";
2094
+ return "🔴 Very Weak";
2095
+ }
2096
+ function calculateNetworkStats(connections, contents) {
2097
+ const connectionCounts = new Map();
2098
+ connections.forEach(conn => {
2099
+ const id1 = conn.source1.id;
2100
+ const id2 = conn.source2.id;
2101
+ connectionCounts.set(id1, (connectionCounts.get(id1) || 0) + 1);
2102
+ connectionCounts.set(id2, (connectionCounts.get(id2) || 0) + 1);
2103
+ });
2104
+ const mostConnectedId = Array.from(connectionCounts.entries())
2105
+ .sort((a, b) => b[1] - a[1])[0]?.[0];
2106
+ const mostConnected = contents.find(c => c.id === mostConnectedId) || contents[0];
2107
+ return {
2108
+ mostConnected: {
2109
+ title: mostConnected?.title || "Unknown",
2110
+ connectionCount: connectionCounts.get(mostConnectedId) || 0,
2111
+ },
2112
+ centralTheme: connections[0]?.sharedConcepts?.[0] || "Unknown",
2113
+ density: connections.length > 0 ? "High" : "Low",
2114
+ };
2115
+ }
2116
+ function extractTypedInsights(results, query, type, minConfidence) {
2117
+ // Simplified insight extraction
2118
+ const insights = [];
2119
+ switch (type) {
2120
+ case "patterns":
2121
+ // Find recurring themes
2122
+ const themes = new Map();
2123
+ results.forEach(result => {
2124
+ if (result.tags) {
2125
+ result.tags.forEach((tag) => {
2126
+ if (!themes.has(tag))
2127
+ themes.set(tag, []);
2128
+ themes.get(tag).push(result);
2129
+ });
2130
+ }
2131
+ });
2132
+ Array.from(themes.entries())
2133
+ .filter(([_, sources]) => sources.length >= 2)
2134
+ .forEach(([theme, sources]) => {
2135
+ insights.push({
2136
+ pattern: theme,
2137
+ frequency: sources.length,
2138
+ confidence: sources.length / results.length,
2139
+ examples: sources.slice(0, 3).map(s => ({
2140
+ text: s.title || "Untitled",
2141
+ source: s.contentType,
2142
+ })),
2143
+ });
2144
+ });
2145
+ break;
2146
+ case "actionable":
2147
+ // Extract actionable items from summaries
2148
+ results.forEach(result => {
2149
+ if (result.summary && typeof result.summary === 'object' && result.summary.actionable_takeaways) {
2150
+ result.summary.actionable_takeaways.forEach((action) => {
2151
+ insights.push({
2152
+ action,
2153
+ reasoning: "Extracted from content analysis",
2154
+ priority: "medium",
2155
+ sources: [result],
2156
+ confidence: 0.8,
2157
+ });
2158
+ });
2159
+ }
2160
+ });
2161
+ break;
2162
+ default:
2163
+ // Generic insight extraction
2164
+ results.slice(0, 5).forEach(result => {
2165
+ if (result.summary) {
2166
+ insights.push({
2167
+ text: typeof result.summary === 'string' ? result.summary : result.summary.one_sentence,
2168
+ source: result.title || "Untitled",
2169
+ confidence: 0.75,
2170
+ });
2171
+ }
2172
+ });
2173
+ }
2174
+ return insights.filter((i) => i.confidence >= minConfidence);
2175
+ }
2176
+ //# sourceMappingURL=index-old.js.map