@telvok/librarian-mcp 1.5.3 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (121) hide show
  1. package/dist/library/errors.d.ts +48 -0
  2. package/dist/library/errors.js +80 -0
  3. package/dist/library/parsers/jsonl.d.ts +9 -4
  4. package/dist/library/parsers/jsonl.js +52 -20
  5. package/dist/library/schemas.d.ts +6 -6
  6. package/dist/library/storage.d.ts +2 -2
  7. package/dist/library/storage.js +2 -2
  8. package/dist/library 2/embeddings.d.ts +21 -0
  9. package/dist/library 2/embeddings.js +86 -0
  10. package/dist/library 2/manager.d.ts +42 -0
  11. package/dist/library 2/manager.js +218 -0
  12. package/dist/library 2/parsers/cursor.d.ts +15 -0
  13. package/dist/library 2/parsers/cursor.js +168 -0
  14. package/dist/library 2/parsers/index.d.ts +6 -0
  15. package/dist/library 2/parsers/index.js +5 -0
  16. package/dist/library 2/parsers/json.d.ts +11 -0
  17. package/dist/library 2/parsers/json.js +95 -0
  18. package/dist/library 2/parsers/jsonl.d.ts +14 -0
  19. package/dist/library 2/parsers/jsonl.js +85 -0
  20. package/dist/library 2/parsers/markdown.d.ts +15 -0
  21. package/dist/library 2/parsers/markdown.js +77 -0
  22. package/dist/library 2/parsers/sqlite.d.ts +8 -0
  23. package/dist/library 2/parsers/sqlite.js +123 -0
  24. package/dist/library 2/parsers/types.d.ts +21 -0
  25. package/dist/library 2/parsers/types.js +4 -0
  26. package/dist/library 2/query.d.ts +26 -0
  27. package/dist/library 2/query.js +104 -0
  28. package/dist/library 2/schemas.d.ts +324 -0
  29. package/dist/library 2/schemas.js +79 -0
  30. package/dist/library 2/storage.d.ts +22 -0
  31. package/dist/library 2/storage.js +36 -0
  32. package/dist/library 2/vector-index.d.ts +55 -0
  33. package/dist/library 2/vector-index.js +160 -0
  34. package/dist/server 2.js +199 -0
  35. package/dist/server.d 2.ts +2 -0
  36. package/dist/server.js +102 -54
  37. package/dist/tools/adopt.d.ts +1 -0
  38. package/dist/tools/adopt.js +37 -10
  39. package/dist/tools/auth.d.ts +69 -0
  40. package/dist/tools/auth.js +379 -0
  41. package/dist/tools/bounty-claim.d.ts +28 -0
  42. package/dist/tools/bounty-claim.js +92 -0
  43. package/dist/tools/bounty-create.d.ts +47 -0
  44. package/dist/tools/bounty-create.js +118 -0
  45. package/dist/tools/bounty-list.d.ts +50 -0
  46. package/dist/tools/bounty-list.js +116 -0
  47. package/dist/tools/bounty-submit.d.ts +34 -0
  48. package/dist/tools/bounty-submit.js +94 -0
  49. package/dist/tools/brief.d.ts +94 -0
  50. package/dist/tools/brief.js +234 -15
  51. package/dist/tools/delete.d.ts +87 -0
  52. package/dist/tools/delete.js +266 -0
  53. package/dist/tools/feedback.d.ts +27 -0
  54. package/dist/tools/feedback.js +98 -0
  55. package/dist/tools/help.d.ts +22 -0
  56. package/dist/tools/help.js +482 -0
  57. package/dist/tools/import-memories.d.ts +1 -0
  58. package/dist/tools/import-memories.js +18 -13
  59. package/dist/tools/index.d.ts +11 -0
  60. package/dist/tools/index.js +12 -0
  61. package/dist/tools/library-buy.d.ts +31 -0
  62. package/dist/tools/library-buy.js +104 -0
  63. package/dist/tools/library-download.d.ts +27 -0
  64. package/dist/tools/library-download.js +177 -0
  65. package/dist/tools/library-publish.d.ts +112 -0
  66. package/dist/tools/library-publish.js +387 -0
  67. package/dist/tools/library-search.d.ts +110 -0
  68. package/dist/tools/library-search.js +132 -0
  69. package/dist/tools/mark-hit.d.ts +1 -0
  70. package/dist/tools/mark-hit.js +83 -5
  71. package/dist/tools/my-books.d.ts +51 -0
  72. package/dist/tools/my-books.js +115 -0
  73. package/dist/tools/my-bounties.d.ts +43 -0
  74. package/dist/tools/my-bounties.js +126 -0
  75. package/dist/tools/rate-book.d.ts +40 -0
  76. package/dist/tools/rate-book.js +147 -0
  77. package/dist/tools/rebuild-index.d.ts +1 -0
  78. package/dist/tools/rebuild-index.js +40 -8
  79. package/dist/tools/record.d.ts +18 -0
  80. package/dist/tools/record.js +30 -26
  81. package/dist/tools/seller-analytics.d.ts +53 -0
  82. package/dist/tools/seller-analytics.js +180 -0
  83. package/dist/tools/sync.d.ts +55 -0
  84. package/dist/tools/sync.js +304 -0
  85. package/dist/tools/unsubscribe.d.ts +48 -0
  86. package/dist/tools/unsubscribe.js +120 -0
  87. package/dist/tools 2/adopt.d.ts +24 -0
  88. package/dist/tools 2/adopt.js +154 -0
  89. package/dist/tools 2/auth.d.ts +35 -0
  90. package/dist/tools 2/auth.js +229 -0
  91. package/dist/tools 2/brief.d.ts +56 -0
  92. package/dist/tools 2/brief.js +414 -0
  93. package/dist/tools 2/help.d.ts +21 -0
  94. package/dist/tools 2/help.js +267 -0
  95. package/dist/tools 2/import-memories.d.ts +32 -0
  96. package/dist/tools 2/import-memories.js +231 -0
  97. package/dist/tools 2/index.d.ts +12 -0
  98. package/dist/tools 2/index.js +12 -0
  99. package/dist/tools 2/mark-hit.d.ts +20 -0
  100. package/dist/tools 2/mark-hit.js +71 -0
  101. package/dist/tools 2/marketplace-buy.d.ts +30 -0
  102. package/dist/tools 2/marketplace-buy.js +97 -0
  103. package/dist/tools 2/marketplace-download.d.ts +26 -0
  104. package/dist/tools 2/marketplace-download.js +160 -0
  105. package/dist/tools 2/marketplace-publish.d.ts +111 -0
  106. package/dist/tools 2/marketplace-publish.js +377 -0
  107. package/dist/tools 2/marketplace-search.d.ts +57 -0
  108. package/dist/tools 2/marketplace-search.js +96 -0
  109. package/dist/tools 2/my-books.d.ts +50 -0
  110. package/dist/tools 2/my-books.js +107 -0
  111. package/dist/tools 2/rate-book.d.ts +39 -0
  112. package/dist/tools 2/rate-book.js +139 -0
  113. package/dist/tools 2/rebuild-index.d.ts +23 -0
  114. package/dist/tools 2/rebuild-index.js +107 -0
  115. package/dist/tools 2/record.d.ts +40 -0
  116. package/dist/tools 2/record.js +205 -0
  117. package/dist/tools 2/seller-analytics.d.ts +35 -0
  118. package/dist/tools 2/seller-analytics.js +102 -0
  119. package/dist/tools 2/sync.d.ts +54 -0
  120. package/dist/tools 2/sync.js +298 -0
  121. package/package.json +1 -1
@@ -0,0 +1,377 @@
1
+ // ============================================================================
2
+ // Marketplace Publish Tool
3
+ // Publish local entries as a book on Telvok marketplace
4
+ // ============================================================================
5
+ import * as fs from 'fs/promises';
6
+ import * as path from 'path';
7
+ import { glob } from 'glob';
8
+ import matter from 'gray-matter';
9
+ import { loadApiKey } from './auth.js';
10
+ import { getLibraryPath, getLocalPath } from '../library/storage.js';
11
+ const TELVOK_API_URL = process.env.TELVOK_API_URL || 'https://telvok.com';
12
+ // ============================================================================
13
+ // Tool Definition
14
+ // ============================================================================
15
+ export const marketplacePublishTool = {
16
+ name: 'marketplace_publish',
17
+ description: `Publish local entries as a book on Telvok marketplace.
18
+
19
+ Collects entries from .librarian/local/ and publishes them as a book.
20
+ Users can browse and purchase your book on telvok.com.
21
+
22
+ Required fields:
23
+ - name: Book title
24
+ - pricing: { type: "open" | "one_time" | "subscription", price_cents? }
25
+ - consumption: How buyers access content ("inline" | "reference" | "download")
26
+ - attestation: { original_work: true, no_secrets: true, terms_accepted: true }
27
+
28
+ Consumption types:
29
+ - "inline": Full content in API responses (all pricing)
30
+ - "reference": README + pointers to entries (all pricing)
31
+ - "download": Download to local library (only for free/open books)
32
+
33
+ Use preview: true to see what would be published without actually publishing.
34
+
35
+ Examples:
36
+ - Preview: marketplace_publish({ name: "My Book", pricing: { type: "open" }, preview: true })
37
+ - Publish: marketplace_publish({ name: "My Book", pricing: { type: "open" }, consumption: "download", attestation: { original_work: true, no_secrets: true, terms_accepted: true } })`,
38
+ inputSchema: {
39
+ type: 'object',
40
+ properties: {
41
+ name: {
42
+ type: 'string',
43
+ description: 'Book title (3-100 characters)',
44
+ },
45
+ description: {
46
+ type: 'string',
47
+ description: 'Short description of the book (optional, max 500 chars)',
48
+ },
49
+ pricing: {
50
+ type: 'object',
51
+ properties: {
52
+ type: {
53
+ type: 'string',
54
+ enum: ['open', 'one_time', 'subscription'],
55
+ description: 'Pricing model',
56
+ },
57
+ price_cents: {
58
+ type: 'number',
59
+ description: 'Price in cents (required for paid, min 100 = $1.00)',
60
+ },
61
+ },
62
+ required: ['type'],
63
+ description: 'Pricing configuration',
64
+ },
65
+ consumption: {
66
+ type: 'string',
67
+ enum: ['inline', 'reference', 'download'],
68
+ description: 'How buyers access content. download only for free books.',
69
+ },
70
+ attestation: {
71
+ type: 'object',
72
+ properties: {
73
+ original_work: { type: 'boolean', description: 'Confirm this is original work' },
74
+ no_secrets: { type: 'boolean', description: 'Confirm no secrets/credentials' },
75
+ terms_accepted: { type: 'boolean', description: 'Accept marketplace terms' },
76
+ },
77
+ required: ['original_work', 'no_secrets', 'terms_accepted'],
78
+ description: 'Required confirmations before publishing',
79
+ },
80
+ preview: {
81
+ type: 'boolean',
82
+ description: 'If true, show what would be published without publishing',
83
+ },
84
+ entries: {
85
+ type: 'array',
86
+ items: { type: 'string' },
87
+ description: 'Specific entry filenames to include (omit for all local/)',
88
+ },
89
+ tags: {
90
+ type: 'array',
91
+ items: { type: 'string' },
92
+ description: 'Category/topic tags (max 10)',
93
+ },
94
+ license: {
95
+ type: 'string',
96
+ enum: ['open', 'open_attributed', 'personal'],
97
+ description: 'License type (default: personal)',
98
+ },
99
+ },
100
+ required: ['name', 'pricing'],
101
+ },
102
+ async handler(args) {
103
+ const { name, description, pricing, consumption, attestation, preview, entries: entryFilter, tags, license } = args;
104
+ // Validate name
105
+ if (!name || typeof name !== 'string' || name.trim().length < 3) {
106
+ throw new Error('Book name is required (minimum 3 characters)');
107
+ }
108
+ if (name.trim().length > 100) {
109
+ throw new Error('Book name must be 100 characters or less');
110
+ }
111
+ // Validate pricing
112
+ if (!pricing || !pricing.type) {
113
+ throw new Error('Pricing type is required');
114
+ }
115
+ if (!['open', 'one_time', 'subscription'].includes(pricing.type)) {
116
+ throw new Error('Pricing type must be: open, one_time, or subscription');
117
+ }
118
+ if (pricing.type !== 'open' && (!pricing.price_cents || pricing.price_cents < 100)) {
119
+ throw new Error('Paid books require price_cents >= 100 ($1.00)');
120
+ }
121
+ // Collect entries from local/ (needed for preview and publish)
122
+ const collectedEntries = await collectLocalEntries(entryFilter);
123
+ if (collectedEntries.length === 0) {
124
+ return {
125
+ success: false,
126
+ message: 'No entries found in .librarian/local/. Use record() to create entries first.',
127
+ };
128
+ }
129
+ // Format pricing display
130
+ const pricingDisplay = pricing.type === 'open'
131
+ ? 'Free'
132
+ : `$${((pricing.price_cents || 0) / 100).toFixed(2)}`;
133
+ // Handle preview mode - return summary without publishing
134
+ if (preview) {
135
+ return {
136
+ success: true,
137
+ preview: true,
138
+ message: `Preview of "${name.trim()}" - NOT published`,
139
+ summary: {
140
+ name: name.trim(),
141
+ pricing: { type: pricing.type, display: pricingDisplay },
142
+ entries_count: collectedEntries.length,
143
+ entries: collectedEntries.map(e => ({
144
+ title: e.title,
145
+ file: path.basename(e.originalPath),
146
+ })),
147
+ },
148
+ next_steps: 'To publish, add consumption type and attestation fields.',
149
+ };
150
+ }
151
+ // Validate consumption type (required for actual publish)
152
+ if (!consumption) {
153
+ return {
154
+ success: false,
155
+ message: 'Consumption type required. Choose how buyers access your content:',
156
+ options: {
157
+ inline: 'Content returned in API responses (best for small entries)',
158
+ reference: 'README + pointers to entries (best for larger books)',
159
+ download: 'Download to local library (only for free/open books)',
160
+ },
161
+ };
162
+ }
163
+ if (!['inline', 'reference', 'download'].includes(consumption)) {
164
+ return {
165
+ success: false,
166
+ message: 'Invalid consumption type. Must be: inline, reference, or download',
167
+ };
168
+ }
169
+ if (consumption === 'download' && pricing.type !== 'open') {
170
+ return {
171
+ success: false,
172
+ message: 'Download is only for free books. Paid content uses inline or reference.',
173
+ next_steps: "Use pricing.type: 'open' for download, or consumption: 'inline'/'reference' for paid.",
174
+ };
175
+ }
176
+ // Validate attestation (required for actual publish)
177
+ if (!attestation) {
178
+ return {
179
+ success: false,
180
+ message: 'Attestation required. Please confirm:',
181
+ required: {
182
+ original_work: 'This is my original work or I have rights to publish',
183
+ no_secrets: 'Contains no secrets, credentials, or sensitive data',
184
+ terms_accepted: 'I accept the Telvok marketplace terms',
185
+ },
186
+ };
187
+ }
188
+ const failedAttestations = [];
189
+ if (!attestation.original_work)
190
+ failedAttestations.push('original_work');
191
+ if (!attestation.no_secrets)
192
+ failedAttestations.push('no_secrets');
193
+ if (!attestation.terms_accepted)
194
+ failedAttestations.push('terms_accepted');
195
+ if (failedAttestations.length > 0) {
196
+ return {
197
+ success: false,
198
+ message: `All attestation fields must be true to publish. Failed: ${failedAttestations.join(', ')}`,
199
+ };
200
+ }
201
+ // Check authentication
202
+ const apiKey = await loadApiKey();
203
+ if (!apiKey) {
204
+ return {
205
+ success: false,
206
+ message: 'Not authenticated. Run auth({ action: "login" }) to connect your Telvok account first.',
207
+ };
208
+ }
209
+ // Format entries for API
210
+ const apiEntries = collectedEntries.map(e => ({
211
+ title: e.title,
212
+ content: e.content,
213
+ intent: e.intent,
214
+ context: e.context,
215
+ reasoning: e.reasoning,
216
+ example: e.example,
217
+ }));
218
+ // Build request body
219
+ const requestBody = {
220
+ name: name.trim(),
221
+ description: description?.trim(),
222
+ pricing,
223
+ consumption,
224
+ entries: apiEntries,
225
+ tags: tags || [],
226
+ license_type: license || 'personal',
227
+ attestation,
228
+ };
229
+ try {
230
+ const response = await fetch(`${TELVOK_API_URL}/api/publish`, {
231
+ method: 'POST',
232
+ headers: {
233
+ 'Authorization': `Bearer ${apiKey}`,
234
+ 'Content-Type': 'application/json',
235
+ },
236
+ body: JSON.stringify(requestBody),
237
+ });
238
+ const data = await response.json();
239
+ if (!response.ok) {
240
+ // Handle Stripe Connect requirement
241
+ if (data.error === 'stripe_connect_required') {
242
+ return {
243
+ success: false,
244
+ message: `Stripe Connect required to sell paid content.\n\nComplete setup at: ${data.setup_url}`,
245
+ setup_url: data.setup_url,
246
+ };
247
+ }
248
+ // Handle validation errors
249
+ if (data.error === 'validation_error') {
250
+ const details = Object.entries(data.details || {})
251
+ .map(([k, v]) => ` - ${k}: ${v}`)
252
+ .join('\n');
253
+ return {
254
+ success: false,
255
+ message: `Validation failed:\n${details}`,
256
+ };
257
+ }
258
+ return {
259
+ success: false,
260
+ message: data.error || `Publish failed: HTTP ${response.status}`,
261
+ };
262
+ }
263
+ return {
264
+ success: true,
265
+ message: data.message || `Published "${data.book?.name}" with ${data.entries_count} entries`,
266
+ book: data.book,
267
+ entries_count: data.entries_count,
268
+ };
269
+ }
270
+ catch (error) {
271
+ const message = error instanceof Error ? error.message : String(error);
272
+ throw new Error(`Publish failed: ${message}`);
273
+ }
274
+ },
275
+ };
276
+ // ============================================================================
277
+ // Entry Collection
278
+ // ============================================================================
279
+ async function collectLocalEntries(filter) {
280
+ const libraryPath = getLibraryPath();
281
+ const localPath = getLocalPath(libraryPath);
282
+ const entries = [];
283
+ try {
284
+ const files = await glob(path.join(localPath, '**/*.md'), { nodir: true });
285
+ for (const filePath of files) {
286
+ const filename = path.basename(filePath);
287
+ // If filter specified, only include matching files
288
+ if (filter && filter.length > 0) {
289
+ const matchesFilter = filter.some(f => filename === f ||
290
+ filename === f + '.md' ||
291
+ filePath.endsWith(f) ||
292
+ filePath.endsWith(f + '.md'));
293
+ if (!matchesFilter)
294
+ continue;
295
+ }
296
+ try {
297
+ const content = await fs.readFile(filePath, 'utf-8');
298
+ const parsed = parseEntryFile(content, filePath);
299
+ if (parsed) {
300
+ entries.push({
301
+ ...parsed,
302
+ originalPath: filePath,
303
+ });
304
+ }
305
+ }
306
+ catch {
307
+ // Skip files that can't be parsed
308
+ }
309
+ }
310
+ }
311
+ catch {
312
+ // No local directory yet
313
+ }
314
+ return entries;
315
+ }
316
+ function parseEntryFile(content, filePath) {
317
+ const { data: frontmatter, content: body } = matter(content);
318
+ const trimmedBody = body.trim();
319
+ if (!trimmedBody)
320
+ return null;
321
+ // Extract title from frontmatter, H1, or filename
322
+ let title = frontmatter.title;
323
+ if (!title) {
324
+ const headingMatch = trimmedBody.match(/^#\s+(.+)$/m);
325
+ if (headingMatch) {
326
+ title = headingMatch[1].trim();
327
+ }
328
+ else {
329
+ // Use filename as title, converting hyphens to spaces
330
+ title = path.basename(filePath, '.md')
331
+ .replace(/-/g, ' ')
332
+ .replace(/\b\w/g, l => l.toUpperCase());
333
+ }
334
+ }
335
+ // Extract sections from body
336
+ const sections = extractSections(trimmedBody);
337
+ return {
338
+ title,
339
+ content: sections.main || trimmedBody,
340
+ intent: frontmatter.intent || undefined,
341
+ context: frontmatter.context || undefined,
342
+ reasoning: sections.reasoning,
343
+ example: sections.example,
344
+ };
345
+ }
346
+ function extractSections(body) {
347
+ const result = {
348
+ main: body,
349
+ };
350
+ // Find ## Reasoning section
351
+ const reasoningMatch = body.match(/##\s*Reasoning\s*\n([\s\S]*?)(?=##|$)/i);
352
+ if (reasoningMatch) {
353
+ result.reasoning = reasoningMatch[1].trim();
354
+ }
355
+ // Find ## Example section
356
+ const exampleMatch = body.match(/##\s*Example\s*\n([\s\S]*?)(?=##|$)/i);
357
+ if (exampleMatch) {
358
+ result.example = exampleMatch[1].trim();
359
+ }
360
+ // Main content is everything after title until first ## section
361
+ const mainMatch = body.match(/^#\s+.+\n\n?([\s\S]*?)(?=##|$)/);
362
+ if (mainMatch) {
363
+ result.main = mainMatch[1].trim();
364
+ }
365
+ else {
366
+ // If no H1 header, take content before first ## section
367
+ const beforeSections = body.match(/^([\s\S]*?)(?=##)/);
368
+ if (beforeSections) {
369
+ result.main = beforeSections[1].trim();
370
+ }
371
+ }
372
+ return result;
373
+ }
374
+ // ============================================================================
375
+ // Export
376
+ // ============================================================================
377
+ export default marketplacePublishTool;
@@ -0,0 +1,57 @@
1
+ interface BookResult {
2
+ slug: string;
3
+ name: string;
4
+ description: string;
5
+ pricing: 'open' | 'one_time' | 'subscription';
6
+ price: string;
7
+ entries: number;
8
+ rating: number | null;
9
+ tags: string[];
10
+ }
11
+ interface SearchResult {
12
+ books: BookResult[];
13
+ total: number;
14
+ message: string;
15
+ }
16
+ export declare const marketplaceSearchTool: {
17
+ name: string;
18
+ description: string;
19
+ inputSchema: {
20
+ type: "object";
21
+ properties: {
22
+ query: {
23
+ type: string;
24
+ description: string;
25
+ };
26
+ filters: {
27
+ type: string;
28
+ properties: {
29
+ pricing: {
30
+ type: string;
31
+ enum: string[];
32
+ description: string;
33
+ };
34
+ tags: {
35
+ type: string;
36
+ items: {
37
+ type: string;
38
+ };
39
+ description: string;
40
+ };
41
+ min_rating: {
42
+ type: string;
43
+ description: string;
44
+ };
45
+ };
46
+ description: string;
47
+ };
48
+ limit: {
49
+ type: string;
50
+ description: string;
51
+ };
52
+ };
53
+ required: never[];
54
+ };
55
+ handler(args: unknown): Promise<SearchResult>;
56
+ };
57
+ export {};
@@ -0,0 +1,96 @@
1
+ // ============================================================================
2
+ // Marketplace Search Tool
3
+ // Search for books on the Telvok marketplace
4
+ // ============================================================================
5
+ const TELVOK_API_URL = process.env.TELVOK_API_URL || 'https://telvok.com';
6
+ // ============================================================================
7
+ // Tool Definition
8
+ // ============================================================================
9
+ export const marketplaceSearchTool = {
10
+ name: 'marketplace_search',
11
+ description: `Search Telvok marketplace for knowledge books.
12
+
13
+ Find books created by other users that you can import into your library.
14
+
15
+ Examples:
16
+ - marketplace_search({ query: "react hooks" })
17
+ - marketplace_search({ query: "python", filters: { pricing: "open" } })
18
+ - marketplace_search({ query: "auth", limit: 5 })
19
+
20
+ Filters:
21
+ - pricing: "open" (free), "one_time" (paid once), "subscription" (ongoing)
22
+ - tags: Array of tags to match
23
+ - min_rating: Minimum quality score (0-5)`,
24
+ inputSchema: {
25
+ type: 'object',
26
+ properties: {
27
+ query: {
28
+ type: 'string',
29
+ description: 'Search terms to find books',
30
+ },
31
+ filters: {
32
+ type: 'object',
33
+ properties: {
34
+ pricing: {
35
+ type: 'string',
36
+ enum: ['open', 'one_time', 'subscription'],
37
+ description: 'Filter by pricing type',
38
+ },
39
+ tags: {
40
+ type: 'array',
41
+ items: { type: 'string' },
42
+ description: 'Filter by tags',
43
+ },
44
+ min_rating: {
45
+ type: 'number',
46
+ description: 'Minimum quality rating (0-5)',
47
+ },
48
+ },
49
+ description: 'Optional filters to narrow results',
50
+ },
51
+ limit: {
52
+ type: 'number',
53
+ description: 'Maximum results to return (default: 10)',
54
+ },
55
+ },
56
+ required: [], // Query is optional - empty returns all books (browse mode)
57
+ },
58
+ async handler(args) {
59
+ const { query = '', filters, limit = 10 } = args;
60
+ try {
61
+ const response = await fetch(`${TELVOK_API_URL}/api/search`, {
62
+ method: 'POST',
63
+ headers: { 'Content-Type': 'application/json' },
64
+ body: JSON.stringify({ query, filters, limit }),
65
+ });
66
+ if (!response.ok) {
67
+ const error = await response.json().catch(() => ({ error: 'Unknown error' }));
68
+ throw new Error(error.error || `Search failed: HTTP ${response.status}`);
69
+ }
70
+ const data = await response.json();
71
+ // Format results for agent consumption
72
+ const books = data.books || [];
73
+ const total = data.total || 0;
74
+ if (books.length === 0) {
75
+ const queryMsg = query ? `for "${query}"` : 'matching your filters';
76
+ return {
77
+ books: [],
78
+ total: 0,
79
+ message: `No books found ${queryMsg}. Try different search terms or filters.`,
80
+ };
81
+ }
82
+ // Build helpful summary
83
+ const summary = books.map((b, i) => `${i + 1}. **${b.name}** (${b.price}) - ${b.entries} entries\n ${b.description?.slice(0, 100)}${b.description?.length > 100 ? '...' : ''}`).join('\n');
84
+ const queryMsg = query ? `for "${query}"` : 'in marketplace';
85
+ return {
86
+ books,
87
+ total,
88
+ message: `Found ${total} book(s) ${queryMsg}:\n\n${summary}\n\nUse marketplace_buy({ slug: "..." }) to purchase a book.`,
89
+ };
90
+ }
91
+ catch (error) {
92
+ const message = error instanceof Error ? error.message : String(error);
93
+ throw new Error(`Marketplace search failed: ${message}`);
94
+ }
95
+ },
96
+ };
@@ -0,0 +1,50 @@
1
+ interface PublishedBook {
2
+ slug: string;
3
+ name: string;
4
+ entries_count: number;
5
+ pricing: {
6
+ type: string;
7
+ display: string;
8
+ };
9
+ created_at: string;
10
+ url: string;
11
+ }
12
+ interface PurchasedBook {
13
+ slug: string;
14
+ name: string;
15
+ author: string;
16
+ entries_count: number;
17
+ pricing: {
18
+ type: string;
19
+ display: string;
20
+ };
21
+ purchased_at: string;
22
+ status: string;
23
+ }
24
+ interface MyBooksResult {
25
+ success: boolean;
26
+ message: string;
27
+ published?: PublishedBook[];
28
+ purchased?: PurchasedBook[];
29
+ summary?: {
30
+ published_count: number;
31
+ purchased_count: number;
32
+ };
33
+ }
34
+ export declare const myBooksTool: {
35
+ name: string;
36
+ description: string;
37
+ inputSchema: {
38
+ type: "object";
39
+ properties: {
40
+ filter: {
41
+ type: string;
42
+ enum: string[];
43
+ description: string;
44
+ };
45
+ };
46
+ required: never[];
47
+ };
48
+ handler(args: unknown): Promise<MyBooksResult>;
49
+ };
50
+ export default myBooksTool;
@@ -0,0 +1,107 @@
1
+ // ============================================================================
2
+ // My Books Tool
3
+ // View published and purchased books from Telvok marketplace
4
+ // ============================================================================
5
+ import { loadApiKey } from './auth.js';
6
+ const TELVOK_API_URL = process.env.TELVOK_API_URL || 'https://telvok.com';
7
+ // ============================================================================
8
+ // Tool Definition
9
+ // ============================================================================
10
+ export const myBooksTool = {
11
+ name: 'my_books',
12
+ description: `View your published and purchased books from Telvok marketplace.
13
+
14
+ Shows:
15
+ - Published: Books you've created and published
16
+ - Purchased: Books you've bought or claimed (active status)
17
+
18
+ Requires authentication. Run auth({ action: "login" }) first if not connected.
19
+
20
+ Examples:
21
+ - my_books() - Show all your books
22
+ - my_books({ filter: "published" }) - Only your published books
23
+ - my_books({ filter: "purchased" }) - Only books you've bought`,
24
+ inputSchema: {
25
+ type: 'object',
26
+ properties: {
27
+ filter: {
28
+ type: 'string',
29
+ enum: ['all', 'published', 'purchased'],
30
+ description: 'Filter results (default: all)',
31
+ },
32
+ },
33
+ required: [],
34
+ },
35
+ async handler(args) {
36
+ const { filter = 'all' } = (args || {});
37
+ // Validate filter
38
+ if (filter && !['all', 'published', 'purchased'].includes(filter)) {
39
+ return {
40
+ success: false,
41
+ message: 'Invalid filter. Must be: all, published, or purchased',
42
+ };
43
+ }
44
+ // Check authentication
45
+ const apiKey = await loadApiKey();
46
+ if (!apiKey) {
47
+ return {
48
+ success: false,
49
+ message: 'Not authenticated. Run auth({ action: "login" }) to connect your Telvok account first.',
50
+ };
51
+ }
52
+ try {
53
+ const url = new URL(`${TELVOK_API_URL}/api/my-books`);
54
+ if (filter && filter !== 'all') {
55
+ url.searchParams.set('filter', filter);
56
+ }
57
+ const response = await fetch(url.toString(), {
58
+ method: 'GET',
59
+ headers: {
60
+ 'Authorization': `Bearer ${apiKey}`,
61
+ },
62
+ });
63
+ const data = await response.json();
64
+ if (!response.ok) {
65
+ return {
66
+ success: false,
67
+ message: data.error || `Failed to fetch books: HTTP ${response.status}`,
68
+ };
69
+ }
70
+ // Format output message
71
+ const publishedCount = data.published?.length || 0;
72
+ const purchasedCount = data.purchased?.length || 0;
73
+ let message = '';
74
+ if (filter === 'published') {
75
+ message = publishedCount === 0
76
+ ? 'You haven\'t published any books yet.'
77
+ : `You have ${publishedCount} published book${publishedCount === 1 ? '' : 's'}.`;
78
+ }
79
+ else if (filter === 'purchased') {
80
+ message = purchasedCount === 0
81
+ ? 'You haven\'t purchased any books yet.'
82
+ : `You have ${purchasedCount} purchased book${purchasedCount === 1 ? '' : 's'}.`;
83
+ }
84
+ else {
85
+ const total = publishedCount + purchasedCount;
86
+ message = total === 0
87
+ ? 'No books yet. Publish with marketplace_publish() or browse with marketplace_search().'
88
+ : `${publishedCount} published, ${purchasedCount} purchased.`;
89
+ }
90
+ return {
91
+ success: true,
92
+ message,
93
+ published: data.published,
94
+ purchased: data.purchased,
95
+ summary: data.summary,
96
+ };
97
+ }
98
+ catch (error) {
99
+ const message = error instanceof Error ? error.message : String(error);
100
+ throw new Error(`Failed to fetch books: ${message}`);
101
+ }
102
+ },
103
+ };
104
+ // ============================================================================
105
+ // Export
106
+ // ============================================================================
107
+ export default myBooksTool;