@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,235 @@
1
+ /**
2
+ * Professional Chart & Data Visualization Design System
3
+ *
4
+ * Based on research from Observable, D3.js, Information is Beautiful,
5
+ * and modern dashboard design systems.
6
+ *
7
+ * Purpose: Enhance prompts with professional design principles to avoid
8
+ * generic AI-generated aesthetics.
9
+ */
10
+ /**
11
+ * Professional color palettes based on industry standards
12
+ */
13
+ const COLOR_SYSTEMS = {
14
+ professional: {
15
+ primary: ['#2563eb', '#7c3aed', '#dc2626', '#ea580c', '#ca8a04'],
16
+ neutral: ['#0f172a', '#475569', '#94a3b8', '#cbd5e1', '#f1f5f9'],
17
+ accent: ['#06b6d4', '#10b981', '#f59e0b'],
18
+ description: 'IBM Carbon, Tailwind-inspired professional palette'
19
+ },
20
+ editorial: {
21
+ primary: ['#e63946', '#f1faee', '#a8dadc', '#457b9d', '#1d3557'],
22
+ neutral: ['#2b2d42', '#8d99ae', '#edf2f4'],
23
+ accent: ['#ff6b6b', '#4ecdc4'],
24
+ description: 'FiveThirtyEight, Economist-inspired editorial colours'
25
+ },
26
+ scientific: {
27
+ primary: ['#003f5c', '#58508d', '#bc5090', '#ff6361', '#ffa600'],
28
+ neutral: ['#1a1a1a', '#4a4a4a', '#8a8a8a', '#cacaca', '#f0f0f0'],
29
+ accent: ['#00d9ff', '#7209b7'],
30
+ description: 'Nature, Science journal-inspired academic palette'
31
+ },
32
+ minimal: {
33
+ primary: ['#000000', '#404040', '#737373', '#a6a6a6', '#d9d9d9'],
34
+ neutral: ['#fafafa', '#f5f5f5', '#e5e5e5'],
35
+ accent: ['#2563eb', '#dc2626'],
36
+ description: 'Edward Tufte-inspired minimal data-ink approach'
37
+ },
38
+ dark: {
39
+ primary: ['#60a5fa', '#a78bfa', '#f472b6', '#fb923c', '#fbbf24'],
40
+ neutral: ['#0a0a0a', '#1f1f1f', '#404040', '#737373', '#a6a6a6'],
41
+ accent: ['#22d3ee', '#34d399'],
42
+ description: 'Observable dark mode, GitHub dark-inspired'
43
+ }
44
+ };
45
+ /**
46
+ * Typography systems based on research findings
47
+ */
48
+ const TYPOGRAPHY_SYSTEMS = {
49
+ professional: {
50
+ title: 'Inter or SF Pro (system-ui fallback)',
51
+ labels: 'Inter or Roboto',
52
+ data: 'JetBrains Mono or SF Mono (monospace)',
53
+ sizes: {
54
+ title: '18-24px',
55
+ subtitle: '14-16px',
56
+ labels: '11-13px',
57
+ annotations: '10-12px'
58
+ }
59
+ },
60
+ editorial: {
61
+ title: 'Playfair Display or Georgia',
62
+ labels: 'Inter or Franklin Gothic',
63
+ data: 'Courier New or monospace',
64
+ sizes: {
65
+ title: '24-32px',
66
+ subtitle: '16-20px',
67
+ labels: '12-14px',
68
+ annotations: '11-13px'
69
+ }
70
+ },
71
+ scientific: {
72
+ title: 'Arial or Helvetica',
73
+ labels: 'Arial or Helvetica',
74
+ data: 'Monaco or Consolas',
75
+ sizes: {
76
+ title: '16-20px',
77
+ subtitle: '13-15px',
78
+ labels: '10-12px',
79
+ annotations: '9-11px'
80
+ }
81
+ }
82
+ };
83
+ /**
84
+ * Design principles based on best practices
85
+ */
86
+ const DESIGN_PRINCIPLES = {
87
+ 'data-ink': `
88
+ • Maximize data-ink ratio (Tufte principle)
89
+ • Remove chartjunk and unnecessary decoration
90
+ • Use subtle gridlines (1px, 10-15% opacity)
91
+ • Direct labeling instead of legends when possible
92
+ • Minimal borders and backgrounds
93
+ `,
94
+ 'storytelling': `
95
+ • Clear visual hierarchy with focal points
96
+ • Progressive disclosure of complexity
97
+ • Annotations and callouts for key insights
98
+ • Contextual reference lines or benchmarks
99
+ • Narrative flow through chart elements
100
+ `,
101
+ 'exploration': `
102
+ • Rich interactive affordances suggested visually
103
+ • Tooltip regions clearly defined
104
+ • Hover states indicated subtly
105
+ • Multiple layers of detail accessibility
106
+ • Clear filtering and selection states
107
+ `,
108
+ 'presentation': `
109
+ • High contrast for projector/screen visibility
110
+ • Large, readable text (minimum 12px)
111
+ • Strong visual anchors and structure
112
+ • Balanced whitespace and breathing room
113
+ • Professional polish without decoration
114
+ `
115
+ };
116
+ /**
117
+ * Chart-specific best practices
118
+ */
119
+ const CHART_SPECIFICS = {
120
+ line: `
121
+ • Line weight: 2-3px for primary, 1-2px for secondary
122
+ • Point markers only at data points, not everywhere
123
+ • Differentiate series with both colour AND line style
124
+ • Y-axis starts at 0 unless justified otherwise
125
+ `,
126
+ bar: `
127
+ • Bar spacing: 10-30% of bar width
128
+ • Sorted by value unless categorical order matters
129
+ • Horizontal bars for long labels
130
+ • Subtle rounded corners (2-4px) for modern feel
131
+ `,
132
+ scatter: `
133
+ • Point size: 4-8px diameter
134
+ • 30-50% opacity for overlapping points
135
+ • Trend lines should be subtle (dashed, lighter colour)
136
+ • Legend shows both shape AND colour
137
+ `,
138
+ pie: `
139
+ • Maximum 5-7 slices (combine small values into "Other")
140
+ • Start at 12 o'clock, arrange clockwise by size
141
+ • Consider donut chart (60-70% inner radius)
142
+ • Direct slice labeling with values
143
+ `,
144
+ heatmap: `
145
+ • Sequential colour scale for continuous data
146
+ • Diverging scale for data with meaningful midpoint
147
+ • Include colour legend with numerical scale
148
+ • Cell annotations for key values
149
+ `
150
+ };
151
+ /**
152
+ * Generate professional design prompt enhancement
153
+ */
154
+ export function enhanceChartPrompt(basePrompt, options = {}) {
155
+ const { colorScheme = 'professional', chartType = 'bar', emphasis = 'data-ink' } = options;
156
+ const colors = COLOR_SYSTEMS[colorScheme];
157
+ const typography = TYPOGRAPHY_SYSTEMS[colorScheme === 'editorial' ? 'editorial' :
158
+ colorScheme === 'scientific' ? 'scientific' : 'professional'];
159
+ const principles = DESIGN_PRINCIPLES[emphasis];
160
+ const specifics = CHART_SPECIFICS[chartType] || '';
161
+ return `${basePrompt}
162
+
163
+ PROFESSIONAL DESIGN SYSTEM:
164
+ Style: ${colors.description}
165
+
166
+ COLOUR PALETTE:
167
+ Primary: ${colors.primary.join(', ')}
168
+ Neutral greys: ${colors.neutral.join(', ')}
169
+ Accent: ${colors.accent.join(', ')}
170
+
171
+ TYPOGRAPHY:
172
+ • Titles: ${typography.title} at ${typography.sizes.title}, font-weight 600
173
+ • Axis labels: ${typography.labels} at ${typography.sizes.labels}, font-weight 500
174
+ • Data labels: ${typography.data} at ${typography.sizes.labels}
175
+ • Annotations: ${typography.labels} at ${typography.sizes.annotations}, font-weight 400
176
+
177
+ DESIGN PRINCIPLES:${principles}
178
+
179
+ CHART-SPECIFIC GUIDELINES:${specifics}
180
+
181
+ LAYOUT & SPACING:
182
+ • Margins: 40-60px (top/bottom), 50-80px (left/right) for axis labels
183
+ • Title placement: top-left or centered, 20-30px from chart area
184
+ • Legend: top-right or bottom, with 16px item spacing
185
+ • Grid: subtle (1px, #000 at 8-12% opacity), aligned to data ticks
186
+
187
+ AVOID:
188
+ ❌ 3D effects, shadows, or gradients on data elements
189
+ ❌ Decorative borders, backgrounds, or textures
190
+ ❌ Bright, saturated neon colours
191
+ ❌ Comic Sans, Papyrus, or novelty fonts
192
+ ❌ Unnecessary chart elements (chartjunk)
193
+ ❌ Default chart library aesthetics (Excel, Google Sheets look)
194
+
195
+ ACHIEVE:
196
+ ✓ Clean, confident, professional presentation
197
+ ✓ Accessibility (WCAG AA contrast ratios minimum)
198
+ ✓ Print-ready quality (300 DPI equivalent)
199
+ ✓ Timeless design that won't look dated
200
+ ✓ Focus on data clarity and insight`;
201
+ }
202
+ /**
203
+ * Get color palette for a specific scheme
204
+ */
205
+ export function getColorPalette(scheme) {
206
+ return COLOR_SYSTEMS[scheme];
207
+ }
208
+ /**
209
+ * Validate chart design prompt for common issues
210
+ */
211
+ export function validateChartPrompt(prompt) {
212
+ const warnings = [];
213
+ const suggestions = [];
214
+ // Check for anti-patterns
215
+ if (prompt.toLowerCase().includes('3d') || prompt.toLowerCase().includes('shadow')) {
216
+ warnings.push('3D effects and shadows reduce data clarity');
217
+ suggestions.push('Use flat design with subtle depth cues instead');
218
+ }
219
+ if (prompt.toLowerCase().includes('gradient') && prompt.includes('data')) {
220
+ warnings.push('Gradients on data elements can be distracting');
221
+ suggestions.push('Reserve gradients for backgrounds only, if at all');
222
+ }
223
+ if (!prompt.toLowerCase().includes('color') && !prompt.toLowerCase().includes('colour')) {
224
+ suggestions.push('Consider specifying a professional colour palette');
225
+ }
226
+ if (!prompt.toLowerCase().includes('font') && !prompt.toLowerCase().includes('typography')) {
227
+ suggestions.push('Specify typography for more professional results');
228
+ }
229
+ return {
230
+ valid: warnings.length === 0,
231
+ warnings,
232
+ suggestions
233
+ };
234
+ }
235
+ //# sourceMappingURL=chart-design-system.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chart-design-system.js","sourceRoot":"","sources":["../../src/utils/chart-design-system.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ export interface CompressResult {
2
+ base64: string;
3
+ mimeType: 'image/jpeg';
4
+ originalBytes: number;
5
+ previewBytes: number;
6
+ wasCompressed: boolean;
7
+ }
8
+ export declare function compressForViewer(base64Data: string, sourceMimeType: string): Promise<CompressResult>;
9
+ //# sourceMappingURL=image-compress.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"image-compress.d.ts","sourceRoot":"","sources":["../../src/utils/image-compress.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,YAAY,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,OAAO,CAAC;CACxB;AAED,wBAAsB,iBAAiB,CACrC,UAAU,EAAE,MAAM,EAClB,cAAc,EAAE,MAAM,GACrB,OAAO,CAAC,cAAc,CAAC,CAyCzB"}
@@ -0,0 +1,43 @@
1
+ import sharp from 'sharp';
2
+ const MAX_VIEWER_DIMENSION = 1024;
3
+ const VIEWER_QUALITY = 100;
4
+ const MAX_VIEWER_BYTES = 400_000;
5
+ export async function compressForViewer(base64Data, sourceMimeType) {
6
+ const inputBuffer = Buffer.from(base64Data, 'base64');
7
+ const originalBytes = inputBuffer.length;
8
+ const outputBuffer = await sharp(inputBuffer)
9
+ .resize(MAX_VIEWER_DIMENSION, MAX_VIEWER_DIMENSION, {
10
+ fit: 'inside',
11
+ withoutEnlargement: true,
12
+ })
13
+ .jpeg({ quality: VIEWER_QUALITY, mozjpeg: true })
14
+ .toBuffer();
15
+ if (outputBuffer.length > MAX_VIEWER_BYTES) {
16
+ for (const quality of [95, 90, 85]) {
17
+ const retryBuffer = await sharp(inputBuffer)
18
+ .resize(MAX_VIEWER_DIMENSION, MAX_VIEWER_DIMENSION, {
19
+ fit: 'inside',
20
+ withoutEnlargement: true,
21
+ })
22
+ .jpeg({ quality, mozjpeg: true })
23
+ .toBuffer();
24
+ if (retryBuffer.length <= MAX_VIEWER_BYTES) {
25
+ return {
26
+ base64: retryBuffer.toString('base64'),
27
+ mimeType: 'image/jpeg',
28
+ originalBytes,
29
+ previewBytes: retryBuffer.length,
30
+ wasCompressed: true,
31
+ };
32
+ }
33
+ }
34
+ }
35
+ return {
36
+ base64: outputBuffer.toString('base64'),
37
+ mimeType: 'image/jpeg',
38
+ originalBytes,
39
+ previewBytes: outputBuffer.length,
40
+ wasCompressed: true,
41
+ };
42
+ }
43
+ //# sourceMappingURL=image-compress.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"image-compress.js","sourceRoot":"","sources":["../../src/utils/image-compress.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,MAAM,oBAAoB,GAAG,IAAI,CAAC;AAClC,MAAM,cAAc,GAAG,GAAG,CAAC;AAC3B,MAAM,gBAAgB,GAAG,OAAO,CAAC;AAUjC,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,UAAkB,EAClB,cAAsB;IAEtB,MAAM,WAAW,GAAG,MAAM,CAAC,IAAI,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;IACtD,MAAM,aAAa,GAAG,WAAW,CAAC,MAAM,CAAC;IAEzC,MAAM,YAAY,GAAG,MAAM,KAAK,CAAC,WAAW,CAAC;SAC1C,MAAM,CAAC,oBAAoB,EAAE,oBAAoB,EAAE;QAClD,GAAG,EAAE,QAAQ;QACb,kBAAkB,EAAE,IAAI;KACzB,CAAC;SACD,IAAI,CAAC,EAAE,OAAO,EAAE,cAAc,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;SAChD,QAAQ,EAAE,CAAC;IAEd,IAAI,YAAY,CAAC,MAAM,GAAG,gBAAgB,EAAE,CAAC;QAC3C,KAAK,MAAM,OAAO,IAAI,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC;YACnC,MAAM,WAAW,GAAG,MAAM,KAAK,CAAC,WAAW,CAAC;iBACzC,MAAM,CAAC,oBAAoB,EAAE,oBAAoB,EAAE;gBAClD,GAAG,EAAE,QAAQ;gBACb,kBAAkB,EAAE,IAAI;aACzB,CAAC;iBACD,IAAI,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;iBAChC,QAAQ,EAAE,CAAC;YAEd,IAAI,WAAW,CAAC,MAAM,IAAI,gBAAgB,EAAE,CAAC;gBAC3C,OAAO;oBACL,MAAM,EAAE,WAAW,CAAC,QAAQ,CAAC,QAAQ,CAAC;oBACtC,QAAQ,EAAE,YAAY;oBACtB,aAAa;oBACb,YAAY,EAAE,WAAW,CAAC,MAAM;oBAChC,aAAa,EAAE,IAAI;iBACpB,CAAC;YACJ,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO;QACL,MAAM,EAAE,YAAY,CAAC,QAAQ,CAAC,QAAQ,CAAC;QACvC,QAAQ,EAAE,YAAY;QACtB,aAAa;QACb,YAAY,EAAE,YAAY,CAAC,MAAM;QACjC,aAAa,EAAE,IAAI;KACpB,CAAC;AACJ,CAAC"}
@@ -0,0 +1,9 @@
1
+ export declare function saveImageToFile(base64Data: string, outputPath: string): Promise<string>;
2
+ export declare function createImagePreviewHtml(imagePath: string, prompt: string, description?: string): Promise<string>;
3
+ export interface ProcessedImage {
4
+ savedPath: string;
5
+ previewPath: string;
6
+ previewImageData: string;
7
+ }
8
+ export declare function processGeneratedImage(base64Data: string, mimeType: string, savePath: string, prompt: string, description?: string): Promise<ProcessedImage>;
9
+ //# sourceMappingURL=image-utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"image-utils.d.ts","sourceRoot":"","sources":["../../src/utils/image-utils.ts"],"names":[],"mappings":"AAKA,wBAAsB,eAAe,CAAC,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAK7F;AAED,wBAAsB,sBAAsB,CAC1C,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,WAAW,CAAC,EAAE,MAAM,GACnB,OAAO,CAAC,MAAM,CAAC,CAsOjB;AAED,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAED,wBAAsB,qBAAqB,CACzC,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,WAAW,CAAC,EAAE,MAAM,GACnB,OAAO,CAAC,cAAc,CAAC,CAkBzB"}
@@ -0,0 +1,257 @@
1
+ import { writeFile, mkdir } from 'fs/promises';
2
+ import { dirname, resolve } from 'path';
3
+ import logger from './logger.js';
4
+ import { compressForViewer } from './image-compress.js';
5
+ export async function saveImageToFile(base64Data, outputPath) {
6
+ const absolutePath = resolve(outputPath);
7
+ await mkdir(dirname(absolutePath), { recursive: true });
8
+ await writeFile(absolutePath, Buffer.from(base64Data, 'base64'));
9
+ return absolutePath;
10
+ }
11
+ export async function createImagePreviewHtml(imagePath, prompt, description) {
12
+ const htmlPath = imagePath.replace(/\.(png|jpg|jpeg|webp)$/i, '.html');
13
+ const imageFilename = imagePath.split(/[/\\]/).pop() || 'image';
14
+ const html = `<!DOCTYPE html>
15
+ <html lang="en">
16
+ <head>
17
+ <meta charset="UTF-8">
18
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
19
+ <title>Gemini Image: ${imageFilename}</title>
20
+ <style>
21
+ * { margin: 0; padding: 0; box-sizing: border-box; }
22
+ body {
23
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
24
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
25
+ min-height: 100vh;
26
+ display: flex;
27
+ align-items: center;
28
+ justify-content: center;
29
+ padding: 20px;
30
+ }
31
+ .container {
32
+ background: white;
33
+ border-radius: 12px;
34
+ box-shadow: 0 20px 60px rgba(0,0,0,0.3);
35
+ max-width: 1200px;
36
+ width: 100%;
37
+ overflow: hidden;
38
+ }
39
+ .header {
40
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
41
+ color: white;
42
+ padding: 30px;
43
+ text-align: center;
44
+ }
45
+ .header h1 {
46
+ font-size: 24px;
47
+ margin-bottom: 10px;
48
+ }
49
+ .header p {
50
+ opacity: 0.9;
51
+ font-size: 14px;
52
+ }
53
+ .content {
54
+ padding: 40px;
55
+ }
56
+ .image-container {
57
+ text-align: center;
58
+ margin-bottom: 30px;
59
+ background: #f8f9fa;
60
+ border-radius: 8px;
61
+ padding: 20px;
62
+ }
63
+ .image-container img {
64
+ max-width: 100%;
65
+ height: auto;
66
+ border-radius: 8px;
67
+ box-shadow: 0 4px 12px rgba(0,0,0,0.1);
68
+ }
69
+ .metadata {
70
+ background: #f8f9fa;
71
+ border-radius: 8px;
72
+ padding: 20px;
73
+ margin-top: 20px;
74
+ }
75
+ .metadata h2 {
76
+ font-size: 18px;
77
+ margin-bottom: 15px;
78
+ color: #333;
79
+ }
80
+ .metadata-item {
81
+ margin-bottom: 15px;
82
+ padding-bottom: 15px;
83
+ border-bottom: 1px solid #e0e0e0;
84
+ }
85
+ .metadata-item:last-child {
86
+ border-bottom: none;
87
+ margin-bottom: 0;
88
+ padding-bottom: 0;
89
+ }
90
+ .metadata-label {
91
+ font-weight: 600;
92
+ color: #667eea;
93
+ display: block;
94
+ margin-bottom: 5px;
95
+ font-size: 14px;
96
+ text-transform: uppercase;
97
+ letter-spacing: 0.5px;
98
+ }
99
+ .metadata-value {
100
+ color: #555;
101
+ line-height: 1.6;
102
+ }
103
+ .prompt-box {
104
+ background: #fff;
105
+ border: 2px solid #667eea;
106
+ border-radius: 6px;
107
+ padding: 15px;
108
+ margin-top: 10px;
109
+ position: relative;
110
+ font-family: 'Consolas', 'Monaco', monospace;
111
+ font-size: 14px;
112
+ line-height: 1.6;
113
+ word-wrap: break-word;
114
+ }
115
+ .copy-btn {
116
+ position: absolute;
117
+ top: 10px;
118
+ right: 10px;
119
+ background: #667eea;
120
+ color: white;
121
+ border: none;
122
+ padding: 6px 12px;
123
+ border-radius: 4px;
124
+ cursor: pointer;
125
+ font-size: 12px;
126
+ font-weight: 600;
127
+ transition: background 0.3s;
128
+ }
129
+ .copy-btn:hover {
130
+ background: #5568d3;
131
+ }
132
+ .copy-btn:active {
133
+ background: #4a5bc4;
134
+ }
135
+ .copy-btn.copied {
136
+ background: #10b981;
137
+ }
138
+ .footer {
139
+ text-align: center;
140
+ padding: 20px;
141
+ color: #999;
142
+ font-size: 12px;
143
+ border-top: 1px solid #e0e0e0;
144
+ }
145
+ .actions {
146
+ display: flex;
147
+ gap: 10px;
148
+ justify-content: center;
149
+ margin-top: 20px;
150
+ }
151
+ .btn {
152
+ background: #667eea;
153
+ color: white;
154
+ border: none;
155
+ padding: 12px 24px;
156
+ border-radius: 6px;
157
+ cursor: pointer;
158
+ font-size: 14px;
159
+ font-weight: 600;
160
+ text-decoration: none;
161
+ display: inline-block;
162
+ transition: background 0.3s;
163
+ }
164
+ .btn:hover {
165
+ background: #5568d3;
166
+ }
167
+ </style>
168
+ </head>
169
+ <body>
170
+ <div class="container">
171
+ <div class="header">
172
+ <h1>Gemini Generated Image</h1>
173
+ <p>Created with Google Gemini AI (Nano Banana Pro)</p>
174
+ </div>
175
+
176
+ <div class="content">
177
+ <div class="image-container">
178
+ <img src="${imageFilename}" alt="Generated image">
179
+ </div>
180
+
181
+ <div class="metadata">
182
+ <h2>Generation Details</h2>
183
+
184
+ <div class="metadata-item">
185
+ <span class="metadata-label">Prompt</span>
186
+ <div class="prompt-box" id="promptBox">
187
+ <button class="copy-btn" onclick="copyPrompt()">Copy</button>
188
+ ${prompt.replace(/</g, '&lt;').replace(/>/g, '&gt;')}
189
+ </div>
190
+ </div>
191
+
192
+ ${description ? `
193
+ <div class="metadata-item">
194
+ <span class="metadata-label">AI Description</span>
195
+ <div class="metadata-value">${description.replace(/</g, '&lt;').replace(/>/g, '&gt;')}</div>
196
+ </div>
197
+ ` : ''}
198
+
199
+ <div class="metadata-item">
200
+ <span class="metadata-label">Generated</span>
201
+ <div class="metadata-value">${new Date().toLocaleString()}</div>
202
+ </div>
203
+
204
+ <div class="metadata-item">
205
+ <span class="metadata-label">File</span>
206
+ <div class="metadata-value">${imageFilename}</div>
207
+ </div>
208
+ </div>
209
+
210
+ <div class="actions">
211
+ <a href="${imageFilename}" download class="btn">Download Image</a>
212
+ </div>
213
+ </div>
214
+
215
+ <div class="footer">
216
+ Generated by Gemini MCP Server &middot; @houtini/gemini-mcp
217
+ </div>
218
+ </div>
219
+
220
+ <script>
221
+ function copyPrompt() {
222
+ const promptText = document.getElementById('promptBox').innerText.replace('Copy', '').trim();
223
+ navigator.clipboard.writeText(promptText).then(() => {
224
+ const btn = document.querySelector('.copy-btn');
225
+ const originalText = btn.textContent;
226
+ btn.textContent = 'Copied!';
227
+ btn.classList.add('copied');
228
+ setTimeout(() => {
229
+ btn.textContent = originalText;
230
+ btn.classList.remove('copied');
231
+ }, 2000);
232
+ });
233
+ }
234
+ </script>
235
+ </body>
236
+ </html>`;
237
+ await writeFile(htmlPath, html, 'utf-8');
238
+ return htmlPath;
239
+ }
240
+ export async function processGeneratedImage(base64Data, mimeType, savePath, prompt, description) {
241
+ const savedPath = await saveImageToFile(base64Data, savePath);
242
+ const previewPath = await createImagePreviewHtml(savedPath, prompt, description);
243
+ const compressed = await compressForViewer(base64Data, mimeType);
244
+ logger.info('Image saved successfully', {
245
+ savedPath,
246
+ previewPath,
247
+ originalKB: Math.round(compressed.originalBytes / 1024),
248
+ previewKB: Math.round(compressed.previewBytes / 1024),
249
+ compressionRatio: (compressed.originalBytes / compressed.previewBytes).toFixed(1) + 'x',
250
+ });
251
+ return {
252
+ savedPath,
253
+ previewPath,
254
+ previewImageData: compressed.base64,
255
+ };
256
+ }
257
+ //# sourceMappingURL=image-utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"image-utils.js","sourceRoot":"","sources":["../../src/utils/image-utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AACxC,OAAO,MAAM,MAAM,aAAa,CAAC;AACjC,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAExD,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,UAAkB,EAAE,UAAkB;IAC1E,MAAM,YAAY,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IACzC,MAAM,KAAK,CAAC,OAAO,CAAC,YAAY,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACxD,MAAM,SAAS,CAAC,YAAY,EAAE,MAAM,CAAC,IAAI,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC,CAAC;IACjE,OAAO,YAAY,CAAC;AACtB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,SAAiB,EACjB,MAAc,EACd,WAAoB;IAEpB,MAAM,QAAQ,GAAG,SAAS,CAAC,OAAO,CAAC,yBAAyB,EAAE,OAAO,CAAC,CAAC;IACvE,MAAM,aAAa,GAAG,SAAS,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,IAAI,OAAO,CAAC;IAEhE,MAAM,IAAI,GAAG;;;;;yBAKU,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;oBA+JlB,aAAa;;;;;;;;;;cAUnB,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;;;;UAItD,WAAW,CAAC,CAAC,CAAC;;;wCAGgB,WAAW,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;;SAEtF,CAAC,CAAC,CAAC,EAAE;;;;wCAI0B,IAAI,IAAI,EAAE,CAAC,cAAc,EAAE;;;;;wCAK3B,aAAa;;;;;mBAKlC,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;QAyBxB,CAAC;IAEP,MAAM,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;IACzC,OAAO,QAAQ,CAAC;AAClB,CAAC;AAQD,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,UAAkB,EAClB,QAAgB,EAChB,QAAgB,EAChB,MAAc,EACd,WAAoB;IAEpB,MAAM,SAAS,GAAG,MAAM,eAAe,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;IAC9D,MAAM,WAAW,GAAG,MAAM,sBAAsB,CAAC,SAAS,EAAE,MAAM,EAAE,WAAW,CAAC,CAAC;IACjF,MAAM,UAAU,GAAG,MAAM,iBAAiB,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;IAEjE,MAAM,CAAC,IAAI,CAAC,0BAA0B,EAAE;QACtC,SAAS;QACT,WAAW;QACX,UAAU,EAAE,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,aAAa,GAAG,IAAI,CAAC;QACvD,SAAS,EAAE,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,YAAY,GAAG,IAAI,CAAC;QACrD,gBAAgB,EAAE,CAAC,UAAU,CAAC,aAAa,GAAG,UAAU,CAAC,YAAY,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,GAAG;KACxF,CAAC,CAAC;IAEH,OAAO;QACL,SAAS;QACT,WAAW;QACX,gBAAgB,EAAE,UAAU,CAAC,MAAM;KACpC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,29 @@
1
+ export interface ImageInput {
2
+ data?: string;
3
+ filePath?: string;
4
+ mimeType?: string;
5
+ /** Thought signature from a previous Gemini image generation/edit call.
6
+ * Must be round-tripped verbatim for conversational editing. */
7
+ thoughtSignature?: string;
8
+ mediaResolution?: string;
9
+ }
10
+ export interface ResolvedImage {
11
+ data: string;
12
+ mimeType: string;
13
+ /** Preserved thought signature for conversational editing. */
14
+ thoughtSignature?: string;
15
+ mediaResolution?: string;
16
+ }
17
+ /**
18
+ * Resolve an image input to base64 data, loading from disk if a filePath
19
+ * is provided. This runs server-side so the image data never transits MCP.
20
+ *
21
+ * Priority: if `data` is provided it is used directly; otherwise `filePath`
22
+ * is read from disk.
23
+ */
24
+ export declare function resolveImageInput(image: ImageInput): Promise<ResolvedImage>;
25
+ /**
26
+ * Resolve an array of image inputs, loading any filePath references from disk.
27
+ */
28
+ export declare function resolveImageInputs(images: ImageInput[]): Promise<ResolvedImage[]>;
29
+ //# sourceMappingURL=resolve-images.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve-images.d.ts","sourceRoot":"","sources":["../../src/utils/resolve-images.ts"],"names":[],"mappings":"AAaA,MAAM,WAAW,UAAU;IACzB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;qEACiE;IACjE,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,8DAA8D;IAC9D,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED;;;;;;GAMG;AACH,wBAAsB,iBAAiB,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,aAAa,CAAC,CAsCjF;AAED;;GAEG;AACH,wBAAsB,kBAAkB,CAAC,MAAM,EAAE,UAAU,EAAE,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC,CAEvF"}
@@ -0,0 +1,56 @@
1
+ import { readFile } from 'fs/promises';
2
+ import { extname, resolve } from 'path';
3
+ import logger from './logger.js';
4
+ const MIME_TYPES = {
5
+ '.jpg': 'image/jpeg',
6
+ '.jpeg': 'image/jpeg',
7
+ '.png': 'image/png',
8
+ '.gif': 'image/gif',
9
+ '.webp': 'image/webp',
10
+ '.bmp': 'image/bmp',
11
+ };
12
+ /**
13
+ * Resolve an image input to base64 data, loading from disk if a filePath
14
+ * is provided. This runs server-side so the image data never transits MCP.
15
+ *
16
+ * Priority: if `data` is provided it is used directly; otherwise `filePath`
17
+ * is read from disk.
18
+ */
19
+ export async function resolveImageInput(image) {
20
+ if (image.data && image.mimeType) {
21
+ return {
22
+ data: image.data,
23
+ mimeType: image.mimeType,
24
+ thoughtSignature: image.thoughtSignature,
25
+ mediaResolution: image.mediaResolution,
26
+ };
27
+ }
28
+ if (image.filePath) {
29
+ const abs = resolve(image.filePath);
30
+ const ext = extname(abs).toLowerCase();
31
+ const mimeType = MIME_TYPES[ext];
32
+ if (!mimeType) {
33
+ throw new Error(`Unsupported image format: ${ext}. Supported: ${Object.keys(MIME_TYPES).join(', ')}`);
34
+ }
35
+ const buffer = await readFile(abs);
36
+ logger.info('Resolved image from file path (server-side)', {
37
+ filePath: abs,
38
+ sizeKB: Math.round(buffer.length / 1024),
39
+ });
40
+ return {
41
+ data: buffer.toString('base64'),
42
+ mimeType,
43
+ thoughtSignature: image.thoughtSignature,
44
+ mediaResolution: image.mediaResolution,
45
+ };
46
+ }
47
+ throw new Error('Image must provide either data+mimeType (base64) or filePath. ' +
48
+ 'Use filePath to bypass MCP transport limits for large images.');
49
+ }
50
+ /**
51
+ * Resolve an array of image inputs, loading any filePath references from disk.
52
+ */
53
+ export async function resolveImageInputs(images) {
54
+ return Promise.all(images.map(resolveImageInput));
55
+ }
56
+ //# sourceMappingURL=resolve-images.js.map