@houtini/gemini-mcp 1.4.5 → 2.2.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 (167) hide show
  1. package/README.md +314 -834
  2. package/claude_desktop_config_example.json +1 -0
  3. package/dist/config/index.d.ts.map +1 -1
  4. package/dist/config/index.js +8 -4
  5. package/dist/config/index.js.map +1 -1
  6. package/dist/config/types.d.ts +5 -0
  7. package/dist/config/types.d.ts.map +1 -1
  8. package/dist/image-viewer/image-viewer-app.html +180 -0
  9. package/dist/image-viewer/src/ui/image-viewer.html +324 -0
  10. package/dist/index-new.d.ts +3 -0
  11. package/dist/index-new.d.ts.map +1 -0
  12. package/dist/index-new.js +7 -0
  13. package/dist/index-new.js.map +1 -0
  14. package/dist/index.d.ts +3 -1
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.js +70 -172
  17. package/dist/index.js.map +1 -1
  18. package/dist/landing-page-viewer/src/ui/landing-page-viewer.html +330 -0
  19. package/dist/services/gemini/export.d.ts +5 -0
  20. package/dist/services/gemini/export.d.ts.map +1 -0
  21. package/dist/services/gemini/export.js +5 -0
  22. package/dist/services/gemini/export.js.map +1 -0
  23. package/dist/services/gemini/image-service.d.ts +45 -0
  24. package/dist/services/gemini/image-service.d.ts.map +1 -0
  25. package/dist/services/gemini/image-service.js +248 -0
  26. package/dist/services/gemini/image-service.js.map +1 -0
  27. package/dist/services/gemini/index.d.ts +7 -2
  28. package/dist/services/gemini/index.d.ts.map +1 -1
  29. package/dist/services/gemini/index.js +132 -56
  30. package/dist/services/gemini/index.js.map +1 -1
  31. package/dist/services/gemini/types.d.ts +32 -0
  32. package/dist/services/gemini/types.d.ts.map +1 -1
  33. package/dist/services/gemini/video-service.d.ts +58 -0
  34. package/dist/services/gemini/video-service.d.ts.map +1 -0
  35. package/dist/services/gemini/video-service.js +325 -0
  36. package/dist/services/gemini/video-service.js.map +1 -0
  37. package/dist/services/media-server.d.ts +28 -0
  38. package/dist/services/media-server.d.ts.map +1 -0
  39. package/dist/services/media-server.js +195 -0
  40. package/dist/services/media-server.js.map +1 -0
  41. package/dist/svg-viewer/src/ui/svg-viewer.html +325 -0
  42. package/dist/tools/gemini-chat.d.ts.map +1 -1
  43. package/dist/tools/gemini-chat.js +7 -1
  44. package/dist/tools/gemini-chat.js.map +1 -1
  45. package/dist/tools/gemini-deep-research.d.ts +1 -2
  46. package/dist/tools/gemini-deep-research.d.ts.map +1 -1
  47. package/dist/tools/gemini-deep-research.js +11 -51
  48. package/dist/tools/gemini-deep-research.js.map +1 -1
  49. package/dist/tools/gemini-help.d.ts +3 -0
  50. package/dist/tools/gemini-help.d.ts.map +1 -0
  51. package/dist/tools/gemini-help.js +534 -0
  52. package/dist/tools/gemini-help.js.map +1 -0
  53. package/dist/tools/gemini-prompt-assistant.d.ts +20 -0
  54. package/dist/tools/gemini-prompt-assistant.d.ts.map +1 -0
  55. package/dist/tools/gemini-prompt-assistant.js +129 -0
  56. package/dist/tools/gemini-prompt-assistant.js.map +1 -0
  57. package/dist/tools/generate-landing-page.d.ts +15 -0
  58. package/dist/tools/generate-landing-page.d.ts.map +1 -0
  59. package/dist/tools/generate-landing-page.js +66 -0
  60. package/dist/tools/generate-landing-page.js.map +1 -0
  61. package/dist/tools/generate-svg.d.ts +14 -0
  62. package/dist/tools/generate-svg.d.ts.map +1 -0
  63. package/dist/tools/generate-svg.js +106 -0
  64. package/dist/tools/generate-svg.js.map +1 -0
  65. package/dist/tools/generate-video.d.ts +24 -0
  66. package/dist/tools/generate-video.d.ts.map +1 -0
  67. package/dist/tools/generate-video.js +163 -0
  68. package/dist/tools/generate-video.js.map +1 -0
  69. package/dist/tools/image-prompt-assistant.d.ts +3 -0
  70. package/dist/tools/image-prompt-assistant.d.ts.map +1 -0
  71. package/dist/tools/image-prompt-assistant.js +790 -0
  72. package/dist/tools/image-prompt-assistant.js.map +1 -0
  73. package/dist/tools/load-image-from-path.d.ts +11 -0
  74. package/dist/tools/load-image-from-path.d.ts.map +1 -0
  75. package/dist/tools/load-image-from-path.js +100 -0
  76. package/dist/tools/load-image-from-path.js.map +1 -0
  77. package/dist/tools/prompt-library/charts.d.ts +325 -0
  78. package/dist/tools/prompt-library/charts.d.ts.map +1 -0
  79. package/dist/tools/prompt-library/charts.js +384 -0
  80. package/dist/tools/prompt-library/charts.js.map +1 -0
  81. package/dist/tools/prompt-library/index.d.ts +8 -0
  82. package/dist/tools/prompt-library/index.d.ts.map +1 -0
  83. package/dist/tools/prompt-library/index.js +10 -0
  84. package/dist/tools/prompt-library/index.js.map +1 -0
  85. package/dist/tools/register-analyze-image.d.ts +3 -0
  86. package/dist/tools/register-analyze-image.d.ts.map +1 -0
  87. package/dist/tools/register-analyze-image.js +67 -0
  88. package/dist/tools/register-analyze-image.js.map +1 -0
  89. package/dist/tools/register-chat.d.ts +3 -0
  90. package/dist/tools/register-chat.d.ts.map +1 -0
  91. package/dist/tools/register-chat.js +71 -0
  92. package/dist/tools/register-chat.js.map +1 -0
  93. package/dist/tools/register-deep-research.d.ts +3 -0
  94. package/dist/tools/register-deep-research.d.ts.map +1 -0
  95. package/dist/tools/register-deep-research.js +59 -0
  96. package/dist/tools/register-deep-research.js.map +1 -0
  97. package/dist/tools/register-describe-image.d.ts +3 -0
  98. package/dist/tools/register-describe-image.d.ts.map +1 -0
  99. package/dist/tools/register-describe-image.js +59 -0
  100. package/dist/tools/register-describe-image.js.map +1 -0
  101. package/dist/tools/register-image-gen.d.ts +3 -0
  102. package/dist/tools/register-image-gen.d.ts.map +1 -0
  103. package/dist/tools/register-image-gen.js +235 -0
  104. package/dist/tools/register-image-gen.js.map +1 -0
  105. package/dist/tools/register-landing-page.d.ts +3 -0
  106. package/dist/tools/register-landing-page.d.ts.map +1 -0
  107. package/dist/tools/register-landing-page.js +79 -0
  108. package/dist/tools/register-landing-page.js.map +1 -0
  109. package/dist/tools/register-list-models.d.ts +3 -0
  110. package/dist/tools/register-list-models.d.ts.map +1 -0
  111. package/dist/tools/register-list-models.js +33 -0
  112. package/dist/tools/register-list-models.js.map +1 -0
  113. package/dist/tools/register-load-image.d.ts +3 -0
  114. package/dist/tools/register-load-image.d.ts.map +1 -0
  115. package/dist/tools/register-load-image.js +66 -0
  116. package/dist/tools/register-load-image.js.map +1 -0
  117. package/dist/tools/register-svg.d.ts +3 -0
  118. package/dist/tools/register-svg.d.ts.map +1 -0
  119. package/dist/tools/register-svg.js +84 -0
  120. package/dist/tools/register-svg.js.map +1 -0
  121. package/dist/tools/register-video.d.ts +3 -0
  122. package/dist/tools/register-video.d.ts.map +1 -0
  123. package/dist/tools/register-video.js +118 -0
  124. package/dist/tools/register-video.js.map +1 -0
  125. package/dist/tools/register-viewers.d.ts +8 -0
  126. package/dist/tools/register-viewers.d.ts.map +1 -0
  127. package/dist/tools/register-viewers.js +89 -0
  128. package/dist/tools/register-viewers.js.map +1 -0
  129. package/dist/tools/schemas.d.ts +33 -0
  130. package/dist/tools/schemas.d.ts.map +1 -0
  131. package/dist/tools/schemas.js +39 -0
  132. package/dist/tools/schemas.js.map +1 -0
  133. package/dist/tools/types.d.ts +12 -0
  134. package/dist/tools/types.d.ts.map +1 -0
  135. package/dist/tools/types.js +2 -0
  136. package/dist/tools/types.js.map +1 -0
  137. package/dist/ui/image-viewer.d.ts +2 -0
  138. package/dist/ui/image-viewer.d.ts.map +1 -0
  139. package/dist/ui/image-viewer.js +42 -0
  140. package/dist/ui/image-viewer.js.map +1 -0
  141. package/dist/utils/chart-design-system.d.ts +92 -0
  142. package/dist/utils/chart-design-system.d.ts.map +1 -0
  143. package/dist/utils/chart-design-system.js +235 -0
  144. package/dist/utils/chart-design-system.js.map +1 -0
  145. package/dist/utils/image-compress.d.ts +9 -0
  146. package/dist/utils/image-compress.d.ts.map +1 -0
  147. package/dist/utils/image-compress.js +43 -0
  148. package/dist/utils/image-compress.js.map +1 -0
  149. package/dist/utils/image-utils.d.ts +9 -0
  150. package/dist/utils/image-utils.d.ts.map +1 -0
  151. package/dist/utils/image-utils.js +257 -0
  152. package/dist/utils/image-utils.js.map +1 -0
  153. package/dist/utils/resolve-images.d.ts +29 -0
  154. package/dist/utils/resolve-images.d.ts.map +1 -0
  155. package/dist/utils/resolve-images.js +56 -0
  156. package/dist/utils/resolve-images.js.map +1 -0
  157. package/dist/utils/tool-wrapper.d.ts +13 -0
  158. package/dist/utils/tool-wrapper.d.ts.map +1 -0
  159. package/dist/utils/tool-wrapper.js +22 -0
  160. package/dist/utils/tool-wrapper.js.map +1 -0
  161. package/dist/utils/video-utils.d.ts +16 -0
  162. package/dist/utils/video-utils.d.ts.map +1 -0
  163. package/dist/utils/video-utils.js +319 -0
  164. package/dist/utils/video-utils.js.map +1 -0
  165. package/dist/video-viewer/src/ui/video-viewer.html +310 -0
  166. package/package.json +21 -7
  167. package/server.json +30 -29
@@ -0,0 +1,325 @@
1
+ import { GeminiError } from '../../utils/error-handler.js';
2
+ import { BaseService } from '../base-service.js';
3
+ import fs from 'fs/promises';
4
+ import path from 'path';
5
+ const GEMINI_API_BASE = 'https://generativelanguage.googleapis.com/v1beta/models';
6
+ export const VIDEO_GENERATION_MODELS = [
7
+ 'veo-3.1-generate-preview',
8
+ ];
9
+ const DEFAULT_VIDEO_MODEL = 'veo-3.1-generate-preview';
10
+ // ─── Retry constants ─────────────────────────────────────────────────────────
11
+ const MAX_429_RETRIES = 3;
12
+ const INITIAL_BACKOFF_MS = 30_000; // 30 seconds
13
+ // ─── Service ─────────────────────────────────────────────────────────────────
14
+ export class GeminiVideoService extends BaseService {
15
+ apiKey;
16
+ defaultModel;
17
+ outputDir;
18
+ pollingIntervalMs;
19
+ maxPollingTimeMs;
20
+ constructor(config, outputDir) {
21
+ super();
22
+ if (!config.apiKey) {
23
+ throw new GeminiError('Missing API key for Gemini video service');
24
+ }
25
+ this.apiKey = config.apiKey;
26
+ this.defaultModel = DEFAULT_VIDEO_MODEL;
27
+ this.outputDir = outputDir || './output';
28
+ // Veo 3.1 video generation takes 2-5 minutes for 1080p 8-second videos
29
+ // Poll every 15 seconds to avoid rate limits
30
+ this.pollingIntervalMs = 15000;
31
+ // Allow up to 10 minutes for video generation
32
+ this.maxPollingTimeMs = 600000;
33
+ this.logInfo('Gemini video service initialised', {
34
+ outputDir: this.outputDir,
35
+ pollingInterval: `${this.pollingIntervalMs / 1000}s`,
36
+ maxPollingTime: `${this.maxPollingTimeMs / 1000}s`
37
+ });
38
+ }
39
+ /**
40
+ * Fetch with automatic retry on 429 (rate limit) responses.
41
+ * Uses exponential backoff starting at INITIAL_BACKOFF_MS.
42
+ */
43
+ async fetchWithRetry(url, init, label = 'request') {
44
+ let lastError;
45
+ for (let attempt = 0; attempt <= MAX_429_RETRIES; attempt++) {
46
+ const response = await fetch(url, init);
47
+ if (response.status !== 429) {
48
+ return response;
49
+ }
50
+ // 429 rate limited
51
+ lastError = new GeminiError(`Video generation rate limited (429). The API has a quota limit. ` +
52
+ `Please wait 30-60 seconds before retrying.`);
53
+ if (attempt < MAX_429_RETRIES) {
54
+ const backoffMs = INITIAL_BACKOFF_MS * Math.pow(2, attempt);
55
+ this.logInfo(`Rate limited (429) on ${label}, retrying in ${backoffMs / 1000}s`, {
56
+ attempt: attempt + 1,
57
+ maxRetries: MAX_429_RETRIES,
58
+ });
59
+ await new Promise(resolve => setTimeout(resolve, backoffMs));
60
+ }
61
+ }
62
+ throw lastError;
63
+ }
64
+ validateModel(model) {
65
+ if (!VIDEO_GENERATION_MODELS.includes(model)) {
66
+ throw new GeminiError(`Invalid video model: ${model}. Allowed: ${VIDEO_GENERATION_MODELS.join(', ')}`);
67
+ }
68
+ }
69
+ buildVeoRequest(options) {
70
+ const instance = {
71
+ prompt: options.prompt
72
+ };
73
+ // Add first frame image if provided (image-to-video)
74
+ if (options.firstFrameImage) {
75
+ instance.image = {
76
+ bytesBase64Encoded: options.firstFrameImage.data,
77
+ mimeType: options.firstFrameImage.mimeType
78
+ };
79
+ }
80
+ // Add reference images if provided (up to 3 for character/style consistency)
81
+ if (options.referenceImages?.length) {
82
+ if (options.referenceImages.length > 3) {
83
+ throw new GeminiError('Maximum 3 reference images allowed');
84
+ }
85
+ instance.referenceImages = options.referenceImages.map(ref => ({
86
+ referenceType: ref.referenceType,
87
+ referenceImage: {
88
+ bytesBase64Encoded: ref.image.data,
89
+ mimeType: ref.image.mimeType
90
+ }
91
+ }));
92
+ }
93
+ const parameters = {
94
+ aspectRatio: options.aspectRatio || '16:9',
95
+ resolution: options.resolution || '1080p',
96
+ durationSeconds: options.durationSeconds || 8,
97
+ sampleCount: options.sampleCount || 1
98
+ };
99
+ if (options.seed !== undefined) {
100
+ parameters.seed = options.seed;
101
+ }
102
+ // Handle audio generation via prompt engineering
103
+ // Gemini API (generativelanguage.googleapis.com) doesn't accept generateAudio parameter
104
+ // Instead, audio is native and controlled via negative prompts
105
+ if (options.generateAudio === false) {
106
+ parameters.negativePrompt = 'audio, sound, noise, speech, dialogue, music, sound effects';
107
+ this.logInfo('Audio generation disabled via negativePrompt');
108
+ }
109
+ return {
110
+ instances: [instance],
111
+ parameters
112
+ };
113
+ }
114
+ async pollOperation(operationName) {
115
+ const startTime = Date.now();
116
+ let attempts = 0;
117
+ while (Date.now() - startTime < this.maxPollingTimeMs) {
118
+ attempts++;
119
+ // The operation name from API is like "models/veo-3.1-generate-preview/operations/12345"
120
+ // Since GEMINI_API_BASE already includes /models, we need to strip it to avoid duplication
121
+ let opPath = operationName;
122
+ if (opPath.startsWith('models/')) {
123
+ opPath = opPath.replace('models/', '');
124
+ }
125
+ const url = `${GEMINI_API_BASE}/${opPath}?key=${this.apiKey}`;
126
+ this.logInfo(`Polling video operation (attempt ${attempts})`, {
127
+ elapsed: `${Math.round((Date.now() - startTime) / 1000)}s`,
128
+ maxTime: `${this.maxPollingTimeMs / 1000}s`
129
+ });
130
+ const response = await this.fetchWithRetry(url, undefined, 'poll operation');
131
+ if (!response.ok) {
132
+ const text = await response.text();
133
+ throw new GeminiError(`Failed to poll operation: ${response.status} ${text}`);
134
+ }
135
+ const data = await response.json();
136
+ if (data.error) {
137
+ throw new GeminiError(`Video generation failed: ${data.error.message}`);
138
+ }
139
+ if (data.done) {
140
+ this.logInfo('Video generation completed', {
141
+ attempts,
142
+ totalTime: `${Math.round((Date.now() - startTime) / 1000)}s`
143
+ });
144
+ return data;
145
+ }
146
+ // Log progress if available
147
+ if (data.metadata?.progressPercent !== undefined) {
148
+ this.logInfo(`Video generation progress: ${data.metadata.progressPercent}%`);
149
+ }
150
+ // Wait before next poll
151
+ await new Promise(resolve => setTimeout(resolve, this.pollingIntervalMs));
152
+ }
153
+ throw new GeminiError(`Video generation timed out after ${this.maxPollingTimeMs / 1000}s. ` +
154
+ `Video generation can take 2-5 minutes for 1080p videos.`);
155
+ }
156
+ async downloadVideoFromUri(uri, mimeType, customPath) {
157
+ this.logInfo('Downloading video from URI', { uri: uri.slice(0, 80) });
158
+ // Download video file using authenticated request
159
+ const response = await this.fetchWithRetry(uri, {
160
+ headers: {
161
+ 'x-goog-api-key': this.apiKey
162
+ }
163
+ }, 'download video');
164
+ if (!response.ok) {
165
+ const text = await response.text();
166
+ throw new GeminiError(`Failed to download video: ${response.status} ${text}`);
167
+ }
168
+ const arrayBuffer = await response.arrayBuffer();
169
+ const buffer = Buffer.from(arrayBuffer);
170
+ // Ensure output directory exists
171
+ await fs.mkdir(this.outputDir, { recursive: true });
172
+ // Determine file extension from mimeType
173
+ const ext = mimeType.includes('mp4') ? 'mp4' : 'webm';
174
+ // Use custom path if provided, otherwise generate timestamped filename
175
+ const filename = customPath
176
+ ? path.basename(customPath)
177
+ : `veo-video-${Date.now()}.${ext}`;
178
+ const fullPath = customPath || path.join(this.outputDir, filename);
179
+ // Write buffer to file
180
+ await fs.writeFile(fullPath, buffer);
181
+ this.logInfo('Video file saved', {
182
+ path: fullPath,
183
+ size: `${Math.round(buffer.length / 1024 / 1024)}MB`
184
+ });
185
+ return path.resolve(fullPath);
186
+ }
187
+ async saveVideoFile(base64Data, mimeType, customPath) {
188
+ // Ensure output directory exists
189
+ await fs.mkdir(this.outputDir, { recursive: true });
190
+ // Determine file extension from mimeType
191
+ const ext = mimeType.includes('mp4') ? 'mp4' : 'webm';
192
+ // Use custom path if provided, otherwise generate timestamped filename
193
+ const filename = customPath
194
+ ? path.basename(customPath)
195
+ : `veo-video-${Date.now()}.${ext}`;
196
+ const fullPath = customPath || path.join(this.outputDir, filename);
197
+ // Convert base64 to buffer and write to file
198
+ const buffer = Buffer.from(base64Data, 'base64');
199
+ await fs.writeFile(fullPath, buffer);
200
+ this.logInfo('Video file saved', {
201
+ path: fullPath,
202
+ size: `${Math.round(buffer.length / 1024 / 1024)}MB`
203
+ });
204
+ return path.resolve(fullPath);
205
+ }
206
+ async generateVideo(options) {
207
+ const model = options.model || this.defaultModel;
208
+ this.validateModel(model);
209
+ this.logInfo('Starting video generation', {
210
+ model,
211
+ prompt: options.prompt.slice(0, 80),
212
+ duration: options.durationSeconds || 8,
213
+ resolution: options.resolution || '1080p',
214
+ aspectRatio: options.aspectRatio || '16:9',
215
+ hasFirstFrame: !!options.firstFrameImage,
216
+ referenceImageCount: options.referenceImages?.length || 0
217
+ });
218
+ // Build request body using Vertex AI format
219
+ const requestBody = this.buildVeoRequest(options);
220
+ // Submit async job
221
+ const submitUrl = `${GEMINI_API_BASE}/${model}:predictLongRunning?key=${this.apiKey}`;
222
+ const submitResponse = await this.fetchWithRetry(submitUrl, {
223
+ method: 'POST',
224
+ headers: { 'Content-Type': 'application/json' },
225
+ body: JSON.stringify(requestBody)
226
+ }, 'submit video');
227
+ if (!submitResponse.ok) {
228
+ const text = await submitResponse.text();
229
+ throw new GeminiError(`Failed to submit video generation: ${submitResponse.status} ${text}`);
230
+ }
231
+ const submitData = await submitResponse.json();
232
+ if (!submitData.name) {
233
+ throw new GeminiError('No operation name returned from video generation submission');
234
+ }
235
+ this.logInfo('Video generation job submitted', {
236
+ operationName: submitData.name
237
+ });
238
+ // Poll for completion
239
+ const completedOperation = await this.pollOperation(submitData.name);
240
+ // Extract video data from response
241
+ // Gemini API returns: response.generateVideoResponse.generatedSamples[0].video.uri
242
+ // Vertex AI returns: response.predictions[0].bytesBase64Encoded
243
+ const response = completedOperation.response;
244
+ if (!response) {
245
+ throw new GeminiError('No response in completed operation');
246
+ }
247
+ // Debug: Log the actual response structure
248
+ this.logInfo('Completed operation response structure:', {
249
+ hasGenerateVideoResponse: !!response.generateVideoResponse,
250
+ hasPredictions: !!response.predictions,
251
+ hasVideos: !!response.videos,
252
+ hasGeneratedSamples: !!response.generatedSamples,
253
+ responseKeys: Object.keys(response),
254
+ fullResponse: JSON.stringify(response, null, 2).slice(0, 500)
255
+ });
256
+ // Try Gemini API format first (URI-based)
257
+ const geminiSample = response.generateVideoResponse?.generatedSamples?.[0];
258
+ const geminiVideo = geminiSample?.video;
259
+ // Try Vertex AI format (base64-based)
260
+ const vertexPrediction = response.predictions?.[0];
261
+ const vertexVideo = response.videos?.[0];
262
+ const altSample = response.generatedSamples?.[0];
263
+ // Handle both URI-based (Gemini API) and base64-based (Vertex AI) responses
264
+ let videoPath;
265
+ let mimeType = 'video/mp4'; // Default mimeType
266
+ if (geminiVideo?.uri) {
267
+ // Gemini API format - download from URI (mimeType often not included, default to mp4)
268
+ this.logInfo('Using Gemini API format (URI-based download)');
269
+ mimeType = geminiVideo.mimeType || 'video/mp4';
270
+ videoPath = await this.downloadVideoFromUri(geminiVideo.uri, mimeType, options.outputPath);
271
+ }
272
+ else if (vertexPrediction?.bytesBase64Encoded && vertexPrediction.mimeType) {
273
+ // Vertex AI predictions format
274
+ this.logInfo('Using Vertex AI predictions format (base64)');
275
+ mimeType = vertexPrediction.mimeType;
276
+ videoPath = await this.saveVideoFile(vertexPrediction.bytesBase64Encoded, mimeType, options.outputPath);
277
+ }
278
+ else if (vertexVideo?.bytesBase64Encoded && vertexVideo.mimeType) {
279
+ // Vertex AI videos format
280
+ this.logInfo('Using Vertex AI videos format (base64)');
281
+ mimeType = vertexVideo.mimeType;
282
+ videoPath = await this.saveVideoFile(vertexVideo.bytesBase64Encoded, mimeType, options.outputPath);
283
+ }
284
+ else if (altSample?.bytesBase64Encoded && altSample.mimeType) {
285
+ // Alternative generatedSamples format
286
+ this.logInfo('Using generatedSamples format (base64)');
287
+ mimeType = altSample.mimeType;
288
+ videoPath = await this.saveVideoFile(altSample.bytesBase64Encoded, altSample.mimeType, options.outputPath);
289
+ }
290
+ else {
291
+ // Provide detailed error with actual response structure for debugging
292
+ const debugInfo = {
293
+ hasGenerateVideoResponse: !!response.generateVideoResponse,
294
+ hasPredictions: !!response.predictions,
295
+ hasVideos: !!response.videos,
296
+ hasGeneratedSamples: !!response.generatedSamples,
297
+ responseKeys: Object.keys(response),
298
+ sampleStructure: JSON.stringify({
299
+ geminiSample,
300
+ geminiVideo,
301
+ vertexPrediction,
302
+ vertexVideo,
303
+ altSample
304
+ }, null, 2)
305
+ };
306
+ throw new GeminiError(`Video response contains neither URI nor base64 data in any recognized format.\n` +
307
+ `Debug info: ${JSON.stringify(debugInfo, null, 2)}\n` +
308
+ `Full response (first 1000 chars): ${JSON.stringify(response, null, 2).slice(0, 1000)}`);
309
+ }
310
+ return {
311
+ videoPath,
312
+ mimeType,
313
+ duration: options.durationSeconds || 8,
314
+ resolution: options.resolution || '1080p',
315
+ aspectRatio: options.aspectRatio || '16:9'
316
+ };
317
+ }
318
+ getDefaultModel() {
319
+ return this.defaultModel;
320
+ }
321
+ getAllowedModels() {
322
+ return VIDEO_GENERATION_MODELS;
323
+ }
324
+ }
325
+ //# sourceMappingURL=video-service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"video-service.js","sourceRoot":"","sources":["../../../src/services/gemini/video-service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Lightweight localhost-only HTTP server for serving generated media files
3
+ * to the MCP App viewer iframes (which cannot access file:// URIs).
4
+ *
5
+ * Security:
6
+ * - Binds to 127.0.0.1 only (no external access)
7
+ * - Random URL token required in path
8
+ * - Path traversal protection (all paths resolved against allowed directory)
9
+ * - Supports HTTP Range requests for video seeking
10
+ */
11
+ export declare class MediaServer {
12
+ private server;
13
+ private port;
14
+ private token;
15
+ private allowedDir;
16
+ constructor(allowedDir: string);
17
+ start(): Promise<void>;
18
+ getPort(): number;
19
+ getBaseUrl(): string;
20
+ /**
21
+ * Convert an absolute file path to a media server URL.
22
+ * Returns null if the path is outside the allowed directory.
23
+ */
24
+ getFileUrl(absPath: string): string | null;
25
+ stop(): void;
26
+ private handleRequest;
27
+ }
28
+ //# sourceMappingURL=media-server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"media-server.d.ts","sourceRoot":"","sources":["../../src/services/media-server.ts"],"names":[],"mappings":"AAqBA;;;;;;;;;GASG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,MAAM,CAAuB;IACrC,OAAO,CAAC,IAAI,CAAK;IACjB,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,UAAU,CAAS;gBAEf,UAAU,EAAE,MAAM;IAKxB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAwB5B,OAAO,IAAI,MAAM;IAIjB,UAAU,IAAI,MAAM;IAIpB;;;OAGG;IACH,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAsB1C,IAAI,IAAI,IAAI;IAQZ,OAAO,CAAC,aAAa;CAoHtB"}
@@ -0,0 +1,195 @@
1
+ import { createServer } from 'http';
2
+ import { stat, createReadStream } from 'fs';
3
+ import { resolve, normalize, extname } from 'path';
4
+ import { randomBytes } from 'crypto';
5
+ import logger from '../utils/logger.js';
6
+ const MIME_TYPES = {
7
+ '.mp4': 'video/mp4',
8
+ '.webm': 'video/webm',
9
+ '.mov': 'video/quicktime',
10
+ '.jpg': 'image/jpeg',
11
+ '.jpeg': 'image/jpeg',
12
+ '.png': 'image/png',
13
+ '.gif': 'image/gif',
14
+ '.webp': 'image/webp',
15
+ '.svg': 'image/svg+xml',
16
+ '.html': 'text/html',
17
+ '.css': 'text/css',
18
+ '.js': 'application/javascript',
19
+ };
20
+ /**
21
+ * Lightweight localhost-only HTTP server for serving generated media files
22
+ * to the MCP App viewer iframes (which cannot access file:// URIs).
23
+ *
24
+ * Security:
25
+ * - Binds to 127.0.0.1 only (no external access)
26
+ * - Random URL token required in path
27
+ * - Path traversal protection (all paths resolved against allowed directory)
28
+ * - Supports HTTP Range requests for video seeking
29
+ */
30
+ export class MediaServer {
31
+ server = null;
32
+ port = 0;
33
+ token;
34
+ allowedDir;
35
+ constructor(allowedDir) {
36
+ this.allowedDir = resolve(allowedDir);
37
+ this.token = randomBytes(16).toString('hex');
38
+ }
39
+ async start() {
40
+ return new Promise((ok, fail) => {
41
+ this.server = createServer((req, res) => this.handleRequest(req, res));
42
+ this.server.on('error', (err) => {
43
+ logger.error('Media server error', { error: err });
44
+ fail(err);
45
+ });
46
+ // Bind to 127.0.0.1 with OS-assigned port
47
+ this.server.listen(0, '127.0.0.1', () => {
48
+ const addr = this.server.address();
49
+ if (addr && typeof addr === 'object') {
50
+ this.port = addr.port;
51
+ }
52
+ logger.info('Media server started', {
53
+ port: this.port,
54
+ baseUrl: this.getBaseUrl(),
55
+ });
56
+ ok();
57
+ });
58
+ });
59
+ }
60
+ getPort() {
61
+ return this.port;
62
+ }
63
+ getBaseUrl() {
64
+ return `http://127.0.0.1:${this.port}/${this.token}`;
65
+ }
66
+ /**
67
+ * Convert an absolute file path to a media server URL.
68
+ * Returns null if the path is outside the allowed directory.
69
+ */
70
+ getFileUrl(absPath) {
71
+ const resolved = resolve(absPath);
72
+ const normalAllowed = normalize(this.allowedDir);
73
+ const normalResolved = normalize(resolved);
74
+ // Ensure the file is within the allowed directory
75
+ if (!normalResolved.startsWith(normalAllowed)) {
76
+ logger.warn('Media server: path outside allowed directory', {
77
+ path: absPath,
78
+ allowedDir: this.allowedDir,
79
+ });
80
+ return null;
81
+ }
82
+ // Build relative path portion (use forward slashes for URL)
83
+ const relative = normalResolved
84
+ .slice(normalAllowed.length)
85
+ .replace(/\\/g, '/');
86
+ return `${this.getBaseUrl()}${relative}`;
87
+ }
88
+ stop() {
89
+ if (this.server) {
90
+ this.server.close();
91
+ this.server = null;
92
+ logger.info('Media server stopped');
93
+ }
94
+ }
95
+ handleRequest(req, res) {
96
+ // CORS headers (safe since localhost-only)
97
+ res.setHeader('Access-Control-Allow-Origin', '*');
98
+ res.setHeader('Access-Control-Allow-Methods', 'GET, HEAD, OPTIONS');
99
+ res.setHeader('Access-Control-Allow-Headers', 'Range');
100
+ res.setHeader('Access-Control-Expose-Headers', 'Content-Range, Accept-Ranges, Content-Length');
101
+ if (req.method === 'OPTIONS') {
102
+ res.writeHead(204);
103
+ res.end();
104
+ return;
105
+ }
106
+ if (req.method !== 'GET' && req.method !== 'HEAD') {
107
+ res.writeHead(405, { 'Content-Type': 'text/plain' });
108
+ res.end('Method Not Allowed');
109
+ return;
110
+ }
111
+ const url = new URL(req.url || '/', `http://127.0.0.1:${this.port}`);
112
+ const pathname = decodeURIComponent(url.pathname);
113
+ // Validate token
114
+ const expectedPrefix = `/${this.token}`;
115
+ if (!pathname.startsWith(expectedPrefix)) {
116
+ res.writeHead(403, { 'Content-Type': 'text/plain' });
117
+ res.end('Forbidden');
118
+ return;
119
+ }
120
+ // Extract the file path after the token
121
+ const relativePath = pathname.slice(expectedPrefix.length) || '/';
122
+ if (relativePath === '/') {
123
+ res.writeHead(200, { 'Content-Type': 'text/plain' });
124
+ res.end('Gemini MCP Media Server');
125
+ return;
126
+ }
127
+ // Resolve to absolute path within allowed directory
128
+ const filePath = resolve(this.allowedDir, '.' + relativePath);
129
+ const normalAllowed = normalize(this.allowedDir);
130
+ const normalFile = normalize(filePath);
131
+ // Path traversal protection
132
+ if (!normalFile.startsWith(normalAllowed)) {
133
+ res.writeHead(403, { 'Content-Type': 'text/plain' });
134
+ res.end('Forbidden');
135
+ return;
136
+ }
137
+ // Get file stats and serve
138
+ stat(filePath, (err, stats) => {
139
+ if (err || !stats.isFile()) {
140
+ res.writeHead(404, { 'Content-Type': 'text/plain' });
141
+ res.end('Not Found');
142
+ return;
143
+ }
144
+ const ext = extname(filePath).toLowerCase();
145
+ const contentType = MIME_TYPES[ext] || 'application/octet-stream';
146
+ const fileSize = stats.size;
147
+ res.setHeader('Accept-Ranges', 'bytes');
148
+ // Handle Range requests (essential for video seeking)
149
+ const rangeHeader = req.headers.range;
150
+ if (rangeHeader) {
151
+ const match = /^bytes=(\d+)-(\d*)$/.exec(rangeHeader);
152
+ if (!match) {
153
+ res.writeHead(416, {
154
+ 'Content-Range': `bytes */${fileSize}`,
155
+ 'Content-Type': 'text/plain',
156
+ });
157
+ res.end('Range Not Satisfiable');
158
+ return;
159
+ }
160
+ const start = parseInt(match[1], 10);
161
+ const end = match[2] ? parseInt(match[2], 10) : fileSize - 1;
162
+ if (start >= fileSize || end >= fileSize || start > end) {
163
+ res.writeHead(416, {
164
+ 'Content-Range': `bytes */${fileSize}`,
165
+ 'Content-Type': 'text/plain',
166
+ });
167
+ res.end('Range Not Satisfiable');
168
+ return;
169
+ }
170
+ res.writeHead(206, {
171
+ 'Content-Range': `bytes ${start}-${end}/${fileSize}`,
172
+ 'Content-Length': end - start + 1,
173
+ 'Content-Type': contentType,
174
+ });
175
+ if (req.method === 'HEAD') {
176
+ res.end();
177
+ return;
178
+ }
179
+ createReadStream(filePath, { start, end }).pipe(res);
180
+ }
181
+ else {
182
+ res.writeHead(200, {
183
+ 'Content-Length': fileSize,
184
+ 'Content-Type': contentType,
185
+ });
186
+ if (req.method === 'HEAD') {
187
+ res.end();
188
+ return;
189
+ }
190
+ createReadStream(filePath).pipe(res);
191
+ }
192
+ });
193
+ }
194
+ }
195
+ //# sourceMappingURL=media-server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"media-server.js","sourceRoot":"","sources":["../../src/services/media-server.ts"],"names":[],"mappings":"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"}