@rlabs-inc/gemini-mcp 0.5.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 (39) hide show
  1. package/LICENCE +21 -0
  2. package/README.md +418 -0
  3. package/dist/gemini-client.d.ts +120 -0
  4. package/dist/gemini-client.js +399 -0
  5. package/dist/index.d.ts +8 -0
  6. package/dist/index.js +220 -0
  7. package/dist/tools/analyze.d.ts +10 -0
  8. package/dist/tools/analyze.js +96 -0
  9. package/dist/tools/brainstorm.d.ts +10 -0
  10. package/dist/tools/brainstorm.js +220 -0
  11. package/dist/tools/cache.d.ts +17 -0
  12. package/dist/tools/cache.js +286 -0
  13. package/dist/tools/code-exec.d.ts +17 -0
  14. package/dist/tools/code-exec.js +135 -0
  15. package/dist/tools/document.d.ts +16 -0
  16. package/dist/tools/document.js +333 -0
  17. package/dist/tools/image-edit.d.ts +16 -0
  18. package/dist/tools/image-edit.js +291 -0
  19. package/dist/tools/image-gen.d.ts +17 -0
  20. package/dist/tools/image-gen.js +148 -0
  21. package/dist/tools/query.d.ts +11 -0
  22. package/dist/tools/query.js +63 -0
  23. package/dist/tools/search.d.ts +15 -0
  24. package/dist/tools/search.js +128 -0
  25. package/dist/tools/speech.d.ts +17 -0
  26. package/dist/tools/speech.js +304 -0
  27. package/dist/tools/structured.d.ts +16 -0
  28. package/dist/tools/structured.js +247 -0
  29. package/dist/tools/summarize.d.ts +10 -0
  30. package/dist/tools/summarize.js +77 -0
  31. package/dist/tools/url-context.d.ts +17 -0
  32. package/dist/tools/url-context.js +226 -0
  33. package/dist/tools/video-gen.d.ts +11 -0
  34. package/dist/tools/video-gen.js +136 -0
  35. package/dist/tools/youtube.d.ts +16 -0
  36. package/dist/tools/youtube.js +218 -0
  37. package/dist/utils/logger.d.ts +33 -0
  38. package/dist/utils/logger.js +82 -0
  39. package/package.json +48 -0
@@ -0,0 +1,17 @@
1
+ /**
2
+ * URL Context Tool - Analyze web pages by URL
3
+ *
4
+ * The URL context tool lets you provide URLs for Gemini to analyze.
5
+ * Useful for:
6
+ * - Extract data from web pages (prices, names, key findings)
7
+ * - Compare documents from multiple URLs
8
+ * - Synthesize content from several sources
9
+ * - Analyze code from GitHub or documentation sites
10
+ *
11
+ * Can be combined with Google Search for powerful workflows.
12
+ */
13
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
14
+ /**
15
+ * Register URL context tools with the MCP server
16
+ */
17
+ export declare function registerUrlContextTool(server: McpServer): void;
@@ -0,0 +1,226 @@
1
+ /**
2
+ * URL Context Tool - Analyze web pages by URL
3
+ *
4
+ * The URL context tool lets you provide URLs for Gemini to analyze.
5
+ * Useful for:
6
+ * - Extract data from web pages (prices, names, key findings)
7
+ * - Compare documents from multiple URLs
8
+ * - Synthesize content from several sources
9
+ * - Analyze code from GitHub or documentation sites
10
+ *
11
+ * Can be combined with Google Search for powerful workflows.
12
+ */
13
+ import { z } from 'zod';
14
+ import { GoogleGenAI } from '@google/genai';
15
+ import { logger } from '../utils/logger.js';
16
+ /**
17
+ * Register URL context tools with the MCP server
18
+ */
19
+ export function registerUrlContextTool(server) {
20
+ server.tool('gemini-analyze-url', {
21
+ urls: z
22
+ .array(z.string())
23
+ .min(1)
24
+ .max(20)
25
+ .describe('URLs to analyze (1-20 URLs)'),
26
+ question: z
27
+ .string()
28
+ .describe('Question about the URLs or task to perform'),
29
+ useGoogleSearch: z
30
+ .boolean()
31
+ .default(false)
32
+ .describe('Also use Google Search to find additional context'),
33
+ }, async ({ urls, question, useGoogleSearch }) => {
34
+ logger.info(`URL context analysis: ${urls.length} URLs`);
35
+ try {
36
+ const apiKey = process.env.GEMINI_API_KEY;
37
+ if (!apiKey) {
38
+ throw new Error('GEMINI_API_KEY not set');
39
+ }
40
+ const genAI = new GoogleGenAI({ apiKey });
41
+ const model = process.env.GEMINI_PRO_MODEL || 'gemini-3-pro-preview';
42
+ // Build the prompt with URLs
43
+ const urlList = urls.map((url, i) => `${i + 1}. ${url}`).join('\n');
44
+ const prompt = `${question}\n\nURLs to analyze:\n${urlList}`;
45
+ // Build tools config
46
+ const tools = [{ urlContext: {} }];
47
+ if (useGoogleSearch) {
48
+ tools.push({ googleSearch: {} });
49
+ }
50
+ // Execute
51
+ const response = await genAI.models.generateContent({
52
+ model,
53
+ contents: prompt,
54
+ config: {
55
+ tools,
56
+ },
57
+ });
58
+ const candidate = response.candidates?.[0];
59
+ if (!candidate) {
60
+ throw new Error('No response from URL analysis');
61
+ }
62
+ let responseText = response.text || '';
63
+ // Add URL retrieval status if available
64
+ const urlContextMetadata = candidate.urlContextMetadata;
65
+ if (urlContextMetadata?.urlMetadata) {
66
+ responseText += '\n\n---\n**URL Retrieval Status:**\n';
67
+ for (const meta of urlContextMetadata.urlMetadata) {
68
+ const status = meta.urlRetrievalStatus === 'URL_RETRIEVAL_STATUS_SUCCESS'
69
+ ? '✓'
70
+ : '✗';
71
+ responseText += `${status} ${meta.retrievedUrl}\n`;
72
+ }
73
+ }
74
+ logger.info('URL context analysis completed');
75
+ return {
76
+ content: [
77
+ {
78
+ type: 'text',
79
+ text: responseText,
80
+ },
81
+ ],
82
+ };
83
+ }
84
+ catch (error) {
85
+ const errorMessage = error instanceof Error ? error.message : String(error);
86
+ logger.error(`Error in URL analysis: ${errorMessage}`);
87
+ return {
88
+ content: [
89
+ {
90
+ type: 'text',
91
+ text: `Error analyzing URLs: ${errorMessage}`,
92
+ },
93
+ ],
94
+ isError: true,
95
+ };
96
+ }
97
+ });
98
+ // Convenience tool for comparing content from multiple URLs
99
+ server.tool('gemini-compare-urls', {
100
+ url1: z.string().describe('First URL to compare'),
101
+ url2: z.string().describe('Second URL to compare'),
102
+ aspect: z
103
+ .string()
104
+ .optional()
105
+ .describe('Specific aspect to compare (e.g., "pricing", "features", "ingredients")'),
106
+ }, async ({ url1, url2, aspect }) => {
107
+ logger.info(`URL comparison: ${url1} vs ${url2}`);
108
+ try {
109
+ const apiKey = process.env.GEMINI_API_KEY;
110
+ if (!apiKey) {
111
+ throw new Error('GEMINI_API_KEY not set');
112
+ }
113
+ const genAI = new GoogleGenAI({ apiKey });
114
+ const model = process.env.GEMINI_FLASH_MODEL || 'gemini-3-flash-preview';
115
+ const prompt = aspect
116
+ ? `Compare the ${aspect} from these two URLs:\n1. ${url1}\n2. ${url2}\n\nProvide a detailed comparison highlighting differences and similarities.`
117
+ : `Compare the content from these two URLs:\n1. ${url1}\n2. ${url2}\n\nProvide a comprehensive comparison of key information from both sources.`;
118
+ const response = await genAI.models.generateContent({
119
+ model,
120
+ contents: prompt,
121
+ config: {
122
+ tools: [{ urlContext: {} }],
123
+ },
124
+ });
125
+ logger.info('URL comparison completed');
126
+ return {
127
+ content: [
128
+ {
129
+ type: 'text',
130
+ text: response.text || 'Unable to compare URLs.',
131
+ },
132
+ ],
133
+ };
134
+ }
135
+ catch (error) {
136
+ const errorMessage = error instanceof Error ? error.message : String(error);
137
+ logger.error(`Error in URL comparison: ${errorMessage}`);
138
+ return {
139
+ content: [
140
+ {
141
+ type: 'text',
142
+ text: `Error comparing URLs: ${errorMessage}`,
143
+ },
144
+ ],
145
+ isError: true,
146
+ };
147
+ }
148
+ });
149
+ // Tool for extracting specific data from URLs
150
+ server.tool('gemini-extract-from-url', {
151
+ url: z.string().describe('URL to extract data from'),
152
+ dataType: z
153
+ .enum(['prices', 'contacts', 'dates', 'products', 'links', 'custom'])
154
+ .describe('Type of data to extract'),
155
+ customFields: z
156
+ .string()
157
+ .optional()
158
+ .describe('For custom extraction: comma-separated fields to extract'),
159
+ }, async ({ url, dataType, customFields }) => {
160
+ logger.info(`URL extraction: ${dataType} from ${url}`);
161
+ try {
162
+ const apiKey = process.env.GEMINI_API_KEY;
163
+ if (!apiKey) {
164
+ throw new Error('GEMINI_API_KEY not set');
165
+ }
166
+ const genAI = new GoogleGenAI({ apiKey });
167
+ const model = process.env.GEMINI_FLASH_MODEL || 'gemini-3-flash-preview';
168
+ // Build prompt based on data type
169
+ let prompt;
170
+ switch (dataType) {
171
+ case 'prices':
172
+ prompt = `Extract all prices and pricing information from this URL: ${url}\n\nReturn as a structured list with item name and price.`;
173
+ break;
174
+ case 'contacts':
175
+ prompt = `Extract all contact information (emails, phone numbers, addresses, social media) from this URL: ${url}`;
176
+ break;
177
+ case 'dates':
178
+ prompt = `Extract all dates, times, and scheduling information from this URL: ${url}`;
179
+ break;
180
+ case 'products':
181
+ prompt = `Extract all product names, descriptions, and details from this URL: ${url}`;
182
+ break;
183
+ case 'links':
184
+ prompt = `Extract all important links from this URL: ${url}\n\nCategorize them by purpose.`;
185
+ break;
186
+ case 'custom':
187
+ if (!customFields) {
188
+ throw new Error('customFields required for custom extraction');
189
+ }
190
+ prompt = `Extract the following information from this URL: ${url}\n\nFields to extract: ${customFields}`;
191
+ break;
192
+ default:
193
+ prompt = `Extract key information from this URL: ${url}`;
194
+ }
195
+ const response = await genAI.models.generateContent({
196
+ model,
197
+ contents: prompt,
198
+ config: {
199
+ tools: [{ urlContext: {} }],
200
+ },
201
+ });
202
+ logger.info('URL extraction completed');
203
+ return {
204
+ content: [
205
+ {
206
+ type: 'text',
207
+ text: response.text || 'No data extracted.',
208
+ },
209
+ ],
210
+ };
211
+ }
212
+ catch (error) {
213
+ const errorMessage = error instanceof Error ? error.message : String(error);
214
+ logger.error(`Error in URL extraction: ${errorMessage}`);
215
+ return {
216
+ content: [
217
+ {
218
+ type: 'text',
219
+ text: `Error extracting from URL: ${errorMessage}`,
220
+ },
221
+ ],
222
+ isError: true,
223
+ };
224
+ }
225
+ });
226
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Video Generation Tool - Generate videos using Gemini's Veo model
3
+ *
4
+ * This tool generates videos from text descriptions. Video generation is async,
5
+ * so we provide tools to start generation and check status.
6
+ */
7
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
8
+ /**
9
+ * Register video generation tools with the MCP server
10
+ */
11
+ export declare function registerVideoGenTool(server: McpServer): void;
@@ -0,0 +1,136 @@
1
+ /**
2
+ * Video Generation Tool - Generate videos using Gemini's Veo model
3
+ *
4
+ * This tool generates videos from text descriptions. Video generation is async,
5
+ * so we provide tools to start generation and check status.
6
+ */
7
+ import { z } from 'zod';
8
+ import { startVideoGeneration, checkVideoStatus, getOutputDir, } from '../gemini-client.js';
9
+ import { logger } from '../utils/logger.js';
10
+ /**
11
+ * Register video generation tools with the MCP server
12
+ */
13
+ export function registerVideoGenTool(server) {
14
+ // Start video generation
15
+ server.tool('gemini-generate-video', {
16
+ prompt: z
17
+ .string()
18
+ .describe('Description of the video to generate (be detailed!)'),
19
+ aspectRatio: z
20
+ .enum(['16:9', '9:16'])
21
+ .default('16:9')
22
+ .describe('Aspect ratio (16:9 for landscape, 9:16 for portrait/mobile)'),
23
+ negativePrompt: z
24
+ .string()
25
+ .optional()
26
+ .describe('Things to avoid in the video (e.g., "text, watermarks, blurry")'),
27
+ }, async ({ prompt, aspectRatio, negativePrompt }) => {
28
+ logger.info(`Starting video generation: ${prompt.substring(0, 50)}...`);
29
+ try {
30
+ const result = await startVideoGeneration(prompt, {
31
+ aspectRatio,
32
+ negativePrompt,
33
+ });
34
+ return {
35
+ content: [
36
+ {
37
+ type: 'text',
38
+ text: `Video generation started!
39
+
40
+ **Operation ID:** \`${result.operationName}\`
41
+ **Status:** ${result.status}
42
+
43
+ Video generation takes 1-5 minutes. Use the \`gemini-check-video\` tool with the operation ID above to check progress and download when complete.
44
+
45
+ **Tip:** Save the operation ID - you'll need it to check status and retrieve the video.`,
46
+ },
47
+ ],
48
+ };
49
+ }
50
+ catch (error) {
51
+ const errorMessage = error instanceof Error ? error.message : String(error);
52
+ logger.error(`Error starting video generation: ${errorMessage}`);
53
+ return {
54
+ content: [
55
+ {
56
+ type: 'text',
57
+ text: `Error starting video generation: ${errorMessage}`,
58
+ },
59
+ ],
60
+ isError: true,
61
+ };
62
+ }
63
+ });
64
+ // Check video generation status
65
+ server.tool('gemini-check-video', {
66
+ operationId: z
67
+ .string()
68
+ .describe('The operation ID returned when starting video generation'),
69
+ }, async ({ operationId }) => {
70
+ logger.info(`Checking video status: ${operationId}`);
71
+ try {
72
+ const result = await checkVideoStatus(operationId);
73
+ if (result.status === 'completed') {
74
+ return {
75
+ content: [
76
+ {
77
+ type: 'text',
78
+ text: `Video generation complete!
79
+
80
+ **Status:** ${result.status}
81
+ ${result.filePath ? `**Saved to:** ${result.filePath}` : ''}
82
+ ${result.videoUri ? `**Video URI:** ${result.videoUri}` : ''}
83
+ **Output directory:** ${getOutputDir()}
84
+
85
+ The video has been downloaded and saved to disk.`,
86
+ },
87
+ ],
88
+ };
89
+ }
90
+ else if (result.status === 'failed') {
91
+ return {
92
+ content: [
93
+ {
94
+ type: 'text',
95
+ text: `Video generation failed.
96
+
97
+ **Status:** ${result.status}
98
+ **Error:** ${result.error || 'Unknown error'}
99
+
100
+ Please try again with a different prompt.`,
101
+ },
102
+ ],
103
+ isError: true,
104
+ };
105
+ }
106
+ else {
107
+ return {
108
+ content: [
109
+ {
110
+ type: 'text',
111
+ text: `Video still generating...
112
+
113
+ **Status:** ${result.status}
114
+ **Operation ID:** ${result.operationName}
115
+
116
+ Please check again in 30-60 seconds using the \`gemini-check-video\` tool.`,
117
+ },
118
+ ],
119
+ };
120
+ }
121
+ }
122
+ catch (error) {
123
+ const errorMessage = error instanceof Error ? error.message : String(error);
124
+ logger.error(`Error checking video status: ${errorMessage}`);
125
+ return {
126
+ content: [
127
+ {
128
+ type: 'text',
129
+ text: `Error checking video status: ${errorMessage}`,
130
+ },
131
+ ],
132
+ isError: true,
133
+ };
134
+ }
135
+ });
136
+ }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * YouTube Analysis Tool - Analyze YouTube videos directly by URL
3
+ *
4
+ * Gemini can process YouTube videos natively, enabling:
5
+ * - Video summarization
6
+ * - Q&A about video content
7
+ * - Timestamp-based analysis
8
+ * - Audio and visual understanding
9
+ *
10
+ * Supports clipping intervals to analyze specific portions of videos.
11
+ */
12
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
13
+ /**
14
+ * Register YouTube analysis tools with the MCP server
15
+ */
16
+ export declare function registerYouTubeTool(server: McpServer): void;
@@ -0,0 +1,218 @@
1
+ /**
2
+ * YouTube Analysis Tool - Analyze YouTube videos directly by URL
3
+ *
4
+ * Gemini can process YouTube videos natively, enabling:
5
+ * - Video summarization
6
+ * - Q&A about video content
7
+ * - Timestamp-based analysis
8
+ * - Audio and visual understanding
9
+ *
10
+ * Supports clipping intervals to analyze specific portions of videos.
11
+ */
12
+ import { z } from 'zod';
13
+ import { GoogleGenAI } from '@google/genai';
14
+ import { logger } from '../utils/logger.js';
15
+ /**
16
+ * Parse time string to seconds (supports formats like "1m30s", "90s", "1:30", "90")
17
+ */
18
+ function parseTimeToSeconds(time) {
19
+ // Handle MM:SS format
20
+ if (time.includes(':')) {
21
+ const [mins, secs] = time.split(':').map(Number);
22
+ return `${mins * 60 + secs}s`;
23
+ }
24
+ // Handle XmYs format
25
+ const minMatch = time.match(/(\d+)m/);
26
+ const secMatch = time.match(/(\d+)s/);
27
+ const mins = minMatch ? parseInt(minMatch[1]) : 0;
28
+ const secs = secMatch ? parseInt(secMatch[1]) : 0;
29
+ if (mins > 0 || secMatch) {
30
+ return `${mins * 60 + secs}s`;
31
+ }
32
+ // Handle plain number (assume seconds)
33
+ const num = parseInt(time);
34
+ if (!isNaN(num)) {
35
+ return `${num}s`;
36
+ }
37
+ return time;
38
+ }
39
+ /**
40
+ * Register YouTube analysis tools with the MCP server
41
+ */
42
+ export function registerYouTubeTool(server) {
43
+ server.tool('gemini-youtube', {
44
+ url: z
45
+ .string()
46
+ .describe('YouTube video URL (e.g., https://www.youtube.com/watch?v=...)'),
47
+ question: z
48
+ .string()
49
+ .describe('Question about the video or task to perform (e.g., "Summarize this video", "What happens at 2:30?")'),
50
+ startTime: z
51
+ .string()
52
+ .optional()
53
+ .describe('Start time for analysis (e.g., "1m30s", "90", "1:30"). Optional.'),
54
+ endTime: z
55
+ .string()
56
+ .optional()
57
+ .describe('End time for analysis (e.g., "5m00s", "300", "5:00"). Optional.'),
58
+ }, async ({ url, question, startTime, endTime }) => {
59
+ logger.info(`YouTube analysis: ${url.substring(0, 50)}...`);
60
+ try {
61
+ const apiKey = process.env.GEMINI_API_KEY;
62
+ if (!apiKey) {
63
+ throw new Error('GEMINI_API_KEY not set');
64
+ }
65
+ // Validate YouTube URL
66
+ if (!url.includes('youtube.com') && !url.includes('youtu.be')) {
67
+ throw new Error('Invalid YouTube URL. Please provide a valid YouTube video link.');
68
+ }
69
+ const genAI = new GoogleGenAI({ apiKey });
70
+ const model = process.env.GEMINI_PRO_MODEL || 'gemini-3-pro-preview';
71
+ // Build the video part with optional clipping
72
+ const videoPart = {
73
+ fileData: {
74
+ fileUri: url,
75
+ mimeType: 'video/*',
76
+ },
77
+ };
78
+ // Add video metadata for clipping if provided
79
+ if (startTime || endTime) {
80
+ const videoMetadata = {};
81
+ if (startTime) {
82
+ videoMetadata.startOffset = parseTimeToSeconds(startTime);
83
+ }
84
+ if (endTime) {
85
+ videoMetadata.endOffset = parseTimeToSeconds(endTime);
86
+ }
87
+ videoPart.videoMetadata = videoMetadata;
88
+ }
89
+ // Build contents
90
+ const contents = [
91
+ {
92
+ role: 'user',
93
+ parts: [videoPart, { text: question }],
94
+ },
95
+ ];
96
+ // Execute
97
+ const response = await genAI.models.generateContent({
98
+ model,
99
+ contents,
100
+ });
101
+ const responseText = response.text || '';
102
+ // Build response with context
103
+ let resultText = responseText;
104
+ if (startTime || endTime) {
105
+ const clipInfo = [];
106
+ if (startTime)
107
+ clipInfo.push(`from ${startTime}`);
108
+ if (endTime)
109
+ clipInfo.push(`to ${endTime}`);
110
+ resultText = `*Analyzed video clip ${clipInfo.join(' ')}*\n\n${responseText}`;
111
+ }
112
+ logger.info('YouTube analysis completed successfully');
113
+ return {
114
+ content: [
115
+ {
116
+ type: 'text',
117
+ text: resultText,
118
+ },
119
+ ],
120
+ };
121
+ }
122
+ catch (error) {
123
+ const errorMessage = error instanceof Error ? error.message : String(error);
124
+ logger.error(`Error in YouTube analysis: ${errorMessage}`);
125
+ return {
126
+ content: [
127
+ {
128
+ type: 'text',
129
+ text: `Error analyzing YouTube video: ${errorMessage}`,
130
+ },
131
+ ],
132
+ isError: true,
133
+ };
134
+ }
135
+ });
136
+ // Convenience tool for YouTube summarization
137
+ server.tool('gemini-youtube-summary', {
138
+ url: z.string().describe('YouTube video URL'),
139
+ style: z
140
+ .enum(['brief', 'detailed', 'bullet-points', 'chapters'])
141
+ .default('brief')
142
+ .describe('Summary style'),
143
+ }, async ({ url, style }) => {
144
+ logger.info(`YouTube summary: ${url.substring(0, 50)}...`);
145
+ try {
146
+ const apiKey = process.env.GEMINI_API_KEY;
147
+ if (!apiKey) {
148
+ throw new Error('GEMINI_API_KEY not set');
149
+ }
150
+ if (!url.includes('youtube.com') && !url.includes('youtu.be')) {
151
+ throw new Error('Invalid YouTube URL');
152
+ }
153
+ const genAI = new GoogleGenAI({ apiKey });
154
+ const model = process.env.GEMINI_FLASH_MODEL || 'gemini-3-flash-preview';
155
+ // Build prompt based on style
156
+ let prompt;
157
+ switch (style) {
158
+ case 'brief':
159
+ prompt = 'Summarize this video in 2-3 sentences.';
160
+ break;
161
+ case 'detailed':
162
+ prompt =
163
+ 'Provide a detailed summary of this video, covering all main points and key takeaways. Include relevant timestamps for important moments.';
164
+ break;
165
+ case 'bullet-points':
166
+ prompt =
167
+ 'Summarize this video as a bullet-point list of key points and takeaways.';
168
+ break;
169
+ case 'chapters':
170
+ prompt =
171
+ 'Create a chapter breakdown of this video with timestamps and descriptions for each section.';
172
+ break;
173
+ default:
174
+ prompt = 'Summarize this video.';
175
+ }
176
+ const contents = [
177
+ {
178
+ role: 'user',
179
+ parts: [
180
+ {
181
+ fileData: {
182
+ fileUri: url,
183
+ mimeType: 'video/*',
184
+ },
185
+ },
186
+ { text: prompt },
187
+ ],
188
+ },
189
+ ];
190
+ const response = await genAI.models.generateContent({
191
+ model,
192
+ contents,
193
+ });
194
+ logger.info('YouTube summary completed successfully');
195
+ return {
196
+ content: [
197
+ {
198
+ type: 'text',
199
+ text: response.text || 'Unable to generate summary.',
200
+ },
201
+ ],
202
+ };
203
+ }
204
+ catch (error) {
205
+ const errorMessage = error instanceof Error ? error.message : String(error);
206
+ logger.error(`Error in YouTube summary: ${errorMessage}`);
207
+ return {
208
+ content: [
209
+ {
210
+ type: 'text',
211
+ text: `Error summarizing YouTube video: ${errorMessage}`,
212
+ },
213
+ ],
214
+ isError: true,
215
+ };
216
+ }
217
+ });
218
+ }
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Logger Utility - Provides logging capabilities with different verbosity levels
3
+ */
4
+ export type LogLevel = "quiet" | "normal" | "verbose";
5
+ interface Logger {
6
+ error: (message: string, ...args: any[]) => void;
7
+ warn: (message: string, ...args: any[]) => void;
8
+ info: (message: string, ...args: any[]) => void;
9
+ debug: (message: string, ...args: any[]) => void;
10
+ prompt: (prompt: string) => void;
11
+ response: (response: string) => void;
12
+ }
13
+ /**
14
+ * Sets up the logger with the specified verbosity level
15
+ */
16
+ export declare function setupLogger(level: LogLevel): void;
17
+ /**
18
+ * Returns whether full prompts and responses should be logged
19
+ */
20
+ export declare function shouldLogFullMessages(): boolean;
21
+ /**
22
+ * Returns whether info messages should be logged
23
+ */
24
+ export declare function shouldLogInfo(): boolean;
25
+ /**
26
+ * Returns whether debug messages should be logged
27
+ */
28
+ export declare function shouldLogDebug(): boolean;
29
+ /**
30
+ * The logger instance
31
+ */
32
+ export declare const logger: Logger;
33
+ export {};