text2image-mcp 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 (47) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +359 -0
  3. package/dist/chat-session.d.ts +20 -0
  4. package/dist/chat-session.d.ts.map +1 -0
  5. package/dist/chat-session.js +54 -0
  6. package/dist/chat-session.js.map +1 -0
  7. package/dist/config.d.ts +13 -0
  8. package/dist/config.d.ts.map +1 -0
  9. package/dist/config.js +56 -0
  10. package/dist/config.js.map +1 -0
  11. package/dist/image-utils.d.ts +12 -0
  12. package/dist/image-utils.d.ts.map +1 -0
  13. package/dist/image-utils.js +48 -0
  14. package/dist/image-utils.js.map +1 -0
  15. package/dist/index.d.ts +3 -0
  16. package/dist/index.d.ts.map +1 -0
  17. package/dist/index.js +5 -0
  18. package/dist/index.js.map +1 -0
  19. package/dist/server.d.ts +11 -0
  20. package/dist/server.d.ts.map +1 -0
  21. package/dist/server.js +98 -0
  22. package/dist/server.js.map +1 -0
  23. package/dist/tools/config-tools.d.ts +7 -0
  24. package/dist/tools/config-tools.d.ts.map +1 -0
  25. package/dist/tools/config-tools.js +48 -0
  26. package/dist/tools/config-tools.js.map +1 -0
  27. package/dist/tools/continue.d.ts +9 -0
  28. package/dist/tools/continue.d.ts.map +1 -0
  29. package/dist/tools/continue.js +111 -0
  30. package/dist/tools/continue.js.map +1 -0
  31. package/dist/tools/edit.d.ts +8 -0
  32. package/dist/tools/edit.d.ts.map +1 -0
  33. package/dist/tools/edit.js +123 -0
  34. package/dist/tools/edit.js.map +1 -0
  35. package/dist/tools/generate.d.ts +9 -0
  36. package/dist/tools/generate.d.ts.map +1 -0
  37. package/dist/tools/generate.js +106 -0
  38. package/dist/tools/generate.js.map +1 -0
  39. package/dist/tools/info.d.ts +4 -0
  40. package/dist/tools/info.d.ts.map +1 -0
  41. package/dist/tools/info.js +38 -0
  42. package/dist/tools/info.js.map +1 -0
  43. package/dist/types.d.ts +25 -0
  44. package/dist/types.d.ts.map +1 -0
  45. package/dist/types.js +19 -0
  46. package/dist/types.js.map +1 -0
  47. package/package.json +63 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Claude Code
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,359 @@
1
+ # Nano-Banana MCP Server 🍌
2
+
3
+ > **🤖 This project was entirely generated by [Claude Code](https://claude.ai/code)** - an AI coding assistant that can create complete, production-ready applications from scratch.
4
+
5
+ A Model Context Protocol (MCP) server that provides AI image generation and editing capabilities using Google's Gemini API. Supports both **Gemini 2.5 Flash** (fast, efficient) and **Gemini 3 Pro** (high quality, 4K, search grounding). Generate stunning images, edit existing ones, and iterate on your creations with simple text prompts.
6
+
7
+ <a href="https://glama.ai/mcp/servers/@ConechoAI/Nano-Banana-MCP">
8
+ <img width="380" height="200" src="https://glama.ai/mcp/servers/@ConechoAI/Nano-Banana-MCP/badge" alt="Nano-Banana-MCP MCP server" />
9
+ </a>
10
+
11
+ ## ✨ Features
12
+
13
+ - 🎨 **Generate Images**: Create new images from text descriptions
14
+ - ✏️ **Edit Images**: Modify existing images with text prompts
15
+ - 🔄 **Multi-Turn Editing**: Continue editing with full conversation context via chat sessions
16
+ - 🖼️ **Multiple Reference Images**: Up to 3 (Flash) or 14 (Pro) reference images for style transfer and guidance
17
+ - 🤖 **Dual Model Support**: Choose between Flash (speed) and Pro (quality) per request
18
+ - 📐 **Aspect Ratios**: 10 aspect ratio options from 1:1 to 21:9
19
+ - 🔍 **Resolution Control**: Generate at 1K, 2K, or 4K resolution (Pro model)
20
+ - 🌐 **Google Search Grounding**: Generate images from real-time data — weather, news, charts (Pro model)
21
+ - 🌍 **Cross-Platform**: Smart file paths for Windows, macOS, and Linux
22
+ - 🔧 **Easy Setup**: Simple configuration with API key
23
+ - 📁 **Auto File Management**: Automatic image saving with organized naming
24
+
25
+ ## 🤖 Models
26
+
27
+ Nano-Banana supports two Gemini models:
28
+
29
+ | Model | ID | Best For |
30
+ |---|---|---|
31
+ | **Flash** (default) | `gemini-2.5-flash-image` | Fast generation, high-volume tasks, free-tier friendly |
32
+ | **Pro** | `gemini-3-pro-image-preview` | High quality, 4K resolution, search grounding, complex prompts |
33
+
34
+ **Auto-upgrade:** If you request Pro-only features (2K/4K resolution, Google Search grounding) while using Flash, the server automatically upgrades to Pro for that request.
35
+
36
+ ## 🔑 Setup
37
+
38
+ 1. **Get your Gemini API key**:
39
+ - Visit [Google AI Studio](https://aistudio.google.com/app/apikey)
40
+ - Create a new API key
41
+ - Copy it for configuration
42
+
43
+ 2. **Configure the MCP server**:
44
+ See configuration examples for your specific client below (Claude Code, Cursor, or other MCP clients).
45
+
46
+ ## 💻 Usage with Claude Code
47
+
48
+ ### Configuration:
49
+
50
+ Add this to your Claude Code MCP settings:
51
+
52
+ **Option A: With environment variable (Recommended - Most Secure)**
53
+ ```json
54
+ {
55
+ "mcpServers": {
56
+ "nano-banana": {
57
+ "command": "npx",
58
+ "args": ["text2image-mcp"],
59
+ "env": {
60
+ "GEMINI_API_KEY": "your-gemini-api-key-here"
61
+ }
62
+ }
63
+ }
64
+ }
65
+ ```
66
+
67
+ **Option B: Without environment variable**
68
+ ```json
69
+ {
70
+ "mcpServers": {
71
+ "nano-banana": {
72
+ "command": "npx",
73
+ "args": ["text2image-mcp"]
74
+ }
75
+ }
76
+ }
77
+ ```
78
+
79
+ ### Usage Examples:
80
+ ```
81
+ Generate an image of a sunset over mountains
82
+ ```
83
+
84
+ ```
85
+ Generate a 4K image of a product mockup using the pro model
86
+ ```
87
+
88
+ ```
89
+ Edit this image to add some birds in the sky with a 16:9 aspect ratio
90
+ ```
91
+
92
+ ```
93
+ Continue editing to make it more dramatic
94
+ ```
95
+
96
+ ## 🎯 Usage with Cursor
97
+
98
+ ### Configuration:
99
+
100
+ Add to your Cursor MCP configuration:
101
+
102
+ **Option A: With environment variable (Recommended)**
103
+ ```json
104
+ {
105
+ "nano-banana": {
106
+ "command": "npx",
107
+ "args": ["text2image-mcp"],
108
+ "env": {
109
+ "GEMINI_API_KEY": "your-gemini-api-key-here"
110
+ }
111
+ }
112
+ }
113
+ ```
114
+
115
+ **Option B: Without environment variable**
116
+ ```json
117
+ {
118
+ "nano-banana": {
119
+ "command": "npx",
120
+ "args": ["text2image-mcp"]
121
+ }
122
+ }
123
+ ```
124
+
125
+ ### Usage Examples:
126
+ - Ask Cursor to generate images for your app
127
+ - Create mockups and prototypes
128
+ - Generate assets for your projects
129
+
130
+ ## 🔧 For Other MCP Clients
131
+
132
+ If you're using a different MCP client, you can configure text2image-mcp using any of these methods:
133
+
134
+ ### Configuration Methods
135
+
136
+ **Method A: Environment Variable in MCP Config (Recommended)**
137
+ ```json
138
+ {
139
+ "nano-banana": {
140
+ "command": "npx",
141
+ "args": ["text2image-mcp"],
142
+ "env": {
143
+ "GEMINI_API_KEY": "your-gemini-api-key-here"
144
+ }
145
+ }
146
+ }
147
+ ```
148
+
149
+ **Method B: System Environment Variable**
150
+ ```bash
151
+ export GEMINI_API_KEY="your-gemini-api-key-here"
152
+ npx text2image-mcp
153
+ ```
154
+
155
+ **Method C: Using the Configure Tool**
156
+ ```bash
157
+ npx text2image-mcp
158
+ # The server will prompt you to configure when first used
159
+ # This creates a local .nano-banana-config.json file
160
+ ```
161
+
162
+ ## 🛠️ Available Commands
163
+
164
+ ### `generate_image`
165
+ Create a new image from a text prompt. Supports model selection, aspect ratios, resolution, and search grounding.
166
+ ```typescript
167
+ generate_image({
168
+ prompt: "A futuristic city at night with neon lights",
169
+ model?: "flash" | "pro", // default: "flash"
170
+ aspectRatio?: "1:1" | "2:3" | "3:2" | "3:4" | "4:3" | "4:5" | "5:4" | "9:16" | "16:9" | "21:9",
171
+ resolution?: "1K" | "2K" | "4K", // Pro model only
172
+ useGoogleSearch?: true // Pro model only — real-time data
173
+ })
174
+ ```
175
+
176
+ ### `edit_image`
177
+ Edit a specific image file with optional reference images, model selection, and output control.
178
+ ```typescript
179
+ edit_image({
180
+ imagePath: "/path/to/image.png",
181
+ prompt: "Add a rainbow in the sky",
182
+ referenceImages?: ["/path/to/reference.jpg"], // up to 3 (Flash) or 14 (Pro)
183
+ model?: "flash" | "pro",
184
+ aspectRatio?: "16:9",
185
+ resolution?: "2K" // Pro model only
186
+ })
187
+ ```
188
+
189
+ ### `continue_editing`
190
+ Continue editing the last generated/edited image with multi-turn conversation context.
191
+ ```typescript
192
+ continue_editing({
193
+ prompt: "Make it more colorful",
194
+ referenceImages?: ["/path/to/style.jpg"],
195
+ aspectRatio?: "16:9",
196
+ resolution?: "2K"
197
+ })
198
+ ```
199
+
200
+ ### `get_last_image_info`
201
+ Get information about the last generated image.
202
+ ```typescript
203
+ get_last_image_info()
204
+ ```
205
+
206
+ ### `configure_gemini_token`
207
+ Configure your Gemini API key.
208
+ ```typescript
209
+ configure_gemini_token({
210
+ apiKey: "your-gemini-api-key"
211
+ })
212
+ ```
213
+
214
+ ### `get_configuration_status`
215
+ Check if the API key is configured.
216
+ ```typescript
217
+ get_configuration_status()
218
+ ```
219
+
220
+ ## 📐 Supported Aspect Ratios
221
+
222
+ | Aspect Ratio | Use Case |
223
+ |---|---|
224
+ | `1:1` | Square — social media posts, profile pictures |
225
+ | `2:3` / `3:2` | Portrait / landscape photos |
226
+ | `3:4` / `4:3` | Classic photo format |
227
+ | `4:5` / `5:4` | Instagram portrait / landscape |
228
+ | `9:16` / `16:9` | Vertical / horizontal video, stories |
229
+ | `21:9` | Ultra-wide, cinematic |
230
+
231
+ ## ⚙️ Configuration Priority
232
+
233
+ The MCP server loads your API key in the following priority order:
234
+
235
+ 1. **🥇 MCP Configuration Environment Variables** (Highest Priority)
236
+ - Set in your `claude_desktop_config.json` or MCP client config
237
+ - Most secure as it's contained within the MCP configuration
238
+ - Example: `"env": { "GEMINI_API_KEY": "your-key" }`
239
+
240
+ 2. **🥈 System Environment Variables**
241
+ - Set in your shell/system environment
242
+ - Example: `export GEMINI_API_KEY="your-key"`
243
+
244
+ 3. **🥉 Local Configuration File** (Lowest Priority)
245
+ - Created when using the `configure_gemini_token` tool
246
+ - Stored as `.nano-banana-config.json` in current directory
247
+ - Automatically ignored by Git and NPM
248
+
249
+ **💡 Recommendation**: Use Method 1 (MCP config env variables) for the best security and convenience.
250
+
251
+ ## 📁 File Storage
252
+
253
+ Images are automatically saved to platform-appropriate locations:
254
+
255
+ - **Windows**: `%USERPROFILE%\Documents\nano-banana-images\`
256
+ - **macOS/Linux**: `./generated_imgs/` (in current directory)
257
+ - **System directories**: `~/nano-banana-images/` (when run from system paths)
258
+
259
+ File naming convention:
260
+ - Generated images: `generated-[timestamp]-[id].png`
261
+ - Edited images: `edited-[timestamp]-[id].png`
262
+
263
+ ## 🎨 Example Workflows
264
+
265
+ ### Basic Image Generation
266
+ 1. `generate_image` - Create your base image
267
+ 2. `continue_editing` - Refine and improve
268
+ 3. `continue_editing` - Add final touches
269
+
270
+ ### High-Resolution Pro Generation
271
+ 1. `generate_image` with `model: "pro"`, `resolution: "4K"` - Create a high-quality base
272
+ 2. `continue_editing` - Iterate with full conversation context
273
+
274
+ ### Search-Grounded Image
275
+ 1. `generate_image` with `useGoogleSearch: true` - Generate from real-time data
276
+ - Example: "Visualize the current weather forecast for Tokyo as an infographic"
277
+ - Example: "Create a chart showing recent tech stock performance"
278
+
279
+ ### Social Media Assets
280
+ 1. `generate_image` with `aspectRatio: "9:16"` - Vertical story format
281
+ 2. `generate_image` with `aspectRatio: "16:9"` - YouTube thumbnail
282
+ 3. `generate_image` with `aspectRatio: "1:1"` - Instagram post
283
+
284
+ ### Style Transfer
285
+ 1. `generate_image` - Create base content
286
+ 2. `edit_image` with reference images - Apply a style from reference
287
+ 3. `continue_editing` - Fine-tune the result
288
+
289
+ ### Iterative Design
290
+ 1. `generate_image` - Start with a concept
291
+ 2. `get_last_image_info` - Check current state
292
+ 3. `continue_editing` - Make adjustments
293
+ 4. Repeat until satisfied
294
+
295
+ ## 🔧 Development
296
+
297
+ This project was created with Claude Code and follows these technologies:
298
+
299
+ - **TypeScript** - Type-safe development
300
+ - **Node.js** - Runtime environment
301
+ - **Zod** - Schema validation
302
+ - **Google GenAI** - Image generation API (Flash + Pro models)
303
+ - **MCP SDK** - Model Context Protocol
304
+
305
+ ### Local Development
306
+
307
+ ```bash
308
+ # Clone the repository
309
+ git clone https://github.com/ConechoAI/Nano-Banana-MCP.git
310
+ cd Nano-Banana-MCP
311
+
312
+ # Install dependencies
313
+ npm install
314
+
315
+ # Run in development mode
316
+ npm run dev
317
+
318
+ # Build for production
319
+ npm run build
320
+
321
+ # Run tests
322
+ npm test
323
+ ```
324
+
325
+ ## 📋 Requirements
326
+
327
+ - Node.js 18.0.0 or higher
328
+ - Gemini API key from Google AI Studio
329
+ - Compatible with Claude Code, Cursor, and other MCP clients
330
+
331
+ ## 🤝 Contributing
332
+
333
+ This project was generated by Claude Code, but contributions are welcome! Please feel free to:
334
+
335
+ - Report bugs
336
+ - Suggest new features
337
+ - Submit pull requests
338
+ - Improve documentation
339
+
340
+ ## 📄 License
341
+
342
+ MIT License - see [LICENSE](LICENSE) file for details.
343
+
344
+ ## 🙏 Acknowledgments
345
+
346
+ - **[Claude Code](https://claude.ai/code)** - For generating this entire project
347
+ - **Google AI** - For the Gemini 2.5 Flash and Gemini 3 Pro Image APIs
348
+ - **Anthropic** - For the Model Context Protocol
349
+ - **Open Source Community** - For the amazing tools and libraries
350
+
351
+ ## 📞 Support
352
+
353
+ - 🐛 **Issues**: [GitHub Issues](https://github.com/ConechoAI/Nano-Banana-MCP/issues)
354
+ - 📖 **Documentation**: This README and inline code comments
355
+ - 💬 **Discussions**: [GitHub Discussions](https://github.com/ConechoAI/Nano-Banana-MCP/discussions)
356
+
357
+ ---
358
+
359
+ *✨ Generated with [Claude Code](https://claude.ai/code)*
@@ -0,0 +1,20 @@
1
+ import { GoogleGenAI } from "@google/genai";
2
+ import { ModelKey, AspectRatio, Resolution } from "./types.js";
3
+ export interface ChatSessionConfig {
4
+ model: ModelKey;
5
+ aspectRatio?: AspectRatio;
6
+ resolution?: Resolution;
7
+ useGoogleSearch?: boolean;
8
+ }
9
+ export declare class ChatSessionManager {
10
+ private chat;
11
+ private currentConfig;
12
+ private genAI;
13
+ constructor(genAI: GoogleGenAI);
14
+ getOrCreateChat(config: ChatSessionConfig): any;
15
+ private createChat;
16
+ sendMessage(message: string, config?: ChatSessionConfig): Promise<any>;
17
+ reset(): void;
18
+ hasActiveSession(): boolean;
19
+ }
20
+ //# sourceMappingURL=chat-session.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chat-session.d.ts","sourceRoot":"","sources":["../src/chat-session.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAU,QAAQ,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAEvE,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,QAAQ,CAAC;IAChB,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,IAAI,CAAa;IACzB,OAAO,CAAC,aAAa,CAAkC;IACvD,OAAO,CAAC,KAAK,CAAc;gBAEf,KAAK,EAAE,WAAW;IAI9B,eAAe,CAAC,MAAM,EAAE,iBAAiB,GAAG,GAAG;IAc/C,OAAO,CAAC,UAAU;IA4BZ,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,GAAG,CAAC;IAK5E,KAAK,IAAI,IAAI;IAKb,gBAAgB,IAAI,OAAO;CAG5B"}
@@ -0,0 +1,54 @@
1
+ import { MODELS } from "./types.js";
2
+ export class ChatSessionManager {
3
+ chat = null;
4
+ currentConfig = null;
5
+ genAI;
6
+ constructor(genAI) {
7
+ this.genAI = genAI;
8
+ }
9
+ getOrCreateChat(config) {
10
+ const configChanged = !this.currentConfig ||
11
+ this.currentConfig.model !== config.model ||
12
+ this.currentConfig.useGoogleSearch !== config.useGoogleSearch;
13
+ if (configChanged || !this.chat) {
14
+ this.chat = this.createChat(config);
15
+ this.currentConfig = { ...config };
16
+ }
17
+ return this.chat;
18
+ }
19
+ createChat(config) {
20
+ const modelId = MODELS[config.model];
21
+ const chatConfig = {
22
+ responseModalities: ["TEXT", "IMAGE"],
23
+ };
24
+ const imageConfig = {};
25
+ if (config.aspectRatio) {
26
+ imageConfig.aspectRatio = config.aspectRatio;
27
+ }
28
+ if (config.resolution && config.model === "pro") {
29
+ imageConfig.imageSize = config.resolution;
30
+ }
31
+ if (Object.keys(imageConfig).length > 0) {
32
+ chatConfig.imageConfig = imageConfig;
33
+ }
34
+ if (config.useGoogleSearch && config.model === "pro") {
35
+ chatConfig.tools = [{ googleSearch: {} }];
36
+ }
37
+ return this.genAI.chats.create({
38
+ model: modelId,
39
+ config: chatConfig,
40
+ });
41
+ }
42
+ async sendMessage(message, config) {
43
+ const chat = this.getOrCreateChat(config || this.currentConfig || { model: "flash" });
44
+ return await chat.sendMessage({ message });
45
+ }
46
+ reset() {
47
+ this.chat = null;
48
+ this.currentConfig = null;
49
+ }
50
+ hasActiveSession() {
51
+ return this.chat !== null;
52
+ }
53
+ }
54
+ //# sourceMappingURL=chat-session.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chat-session.js","sourceRoot":"","sources":["../src/chat-session.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,MAAM,EAAqC,MAAM,YAAY,CAAC;AASvE,MAAM,OAAO,kBAAkB;IACrB,IAAI,GAAQ,IAAI,CAAC;IACjB,aAAa,GAA6B,IAAI,CAAC;IAC/C,KAAK,CAAc;IAE3B,YAAY,KAAkB;QAC5B,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IACrB,CAAC;IAED,eAAe,CAAC,MAAyB;QACvC,MAAM,aAAa,GACjB,CAAC,IAAI,CAAC,aAAa;YACnB,IAAI,CAAC,aAAa,CAAC,KAAK,KAAK,MAAM,CAAC,KAAK;YACzC,IAAI,CAAC,aAAa,CAAC,eAAe,KAAK,MAAM,CAAC,eAAe,CAAC;QAEhE,IAAI,aAAa,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;YAChC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;YACpC,IAAI,CAAC,aAAa,GAAG,EAAE,GAAG,MAAM,EAAE,CAAC;QACrC,CAAC;QAED,OAAO,IAAI,CAAC,IAAI,CAAC;IACnB,CAAC;IAEO,UAAU,CAAC,MAAyB;QAC1C,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAErC,MAAM,UAAU,GAAQ;YACtB,kBAAkB,EAAE,CAAC,MAAM,EAAE,OAAO,CAAC;SACtC,CAAC;QAEF,MAAM,WAAW,GAAQ,EAAE,CAAC;QAC5B,IAAI,MAAM,CAAC,WAAW,EAAE,CAAC;YACvB,WAAW,CAAC,WAAW,GAAG,MAAM,CAAC,WAAW,CAAC;QAC/C,CAAC;QACD,IAAI,MAAM,CAAC,UAAU,IAAI,MAAM,CAAC,KAAK,KAAK,KAAK,EAAE,CAAC;YAChD,WAAW,CAAC,SAAS,GAAG,MAAM,CAAC,UAAU,CAAC;QAC5C,CAAC;QACD,IAAI,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACxC,UAAU,CAAC,WAAW,GAAG,WAAW,CAAC;QACvC,CAAC;QAED,IAAI,MAAM,CAAC,eAAe,IAAI,MAAM,CAAC,KAAK,KAAK,KAAK,EAAE,CAAC;YACrD,UAAU,CAAC,KAAK,GAAG,CAAC,EAAE,YAAY,EAAE,EAAE,EAAE,CAAC,CAAC;QAC5C,CAAC;QAED,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC;YAC7B,KAAK,EAAE,OAAO;YACd,MAAM,EAAE,UAAU;SACnB,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,OAAe,EAAE,MAA0B;QAC3D,MAAM,IAAI,GAAG,IAAI,CAAC,eAAe,CAAC,MAAM,IAAI,IAAI,CAAC,aAAa,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC;QACtF,OAAO,MAAM,IAAI,CAAC,WAAW,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC;IAC7C,CAAC;IAED,KAAK;QACH,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC;IAC5B,CAAC;IAED,gBAAgB;QACd,OAAO,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC;IAC5B,CAAC;CACF"}
@@ -0,0 +1,13 @@
1
+ import { GoogleGenAI } from "@google/genai";
2
+ import { Config } from "./types.js";
3
+ export type ConfigSource = "environment" | "config_file" | "not_configured";
4
+ export declare class ConfigManager {
5
+ config: Config | null;
6
+ genAI: GoogleGenAI | null;
7
+ configSource: ConfigSource;
8
+ isConfigured(): boolean;
9
+ load(): Promise<void>;
10
+ setApiKey(apiKey: string): Promise<void>;
11
+ private save;
12
+ }
13
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAK5C,OAAO,EAAE,MAAM,EAAgB,MAAM,YAAY,CAAC;AAIlD,MAAM,MAAM,YAAY,GAAG,aAAa,GAAG,aAAa,GAAG,gBAAgB,CAAC;AAE5E,qBAAa,aAAa;IACxB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAQ;IAC7B,KAAK,EAAE,WAAW,GAAG,IAAI,CAAQ;IACjC,YAAY,EAAE,YAAY,CAAoB;IAE9C,YAAY,IAAI,OAAO;IAIjB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IA2BrB,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;YAQhC,IAAI;CAMnB"}
package/dist/config.js ADDED
@@ -0,0 +1,56 @@
1
+ // src/config.ts
2
+ import { GoogleGenAI } from "@google/genai";
3
+ import { config as dotenvConfig } from "dotenv";
4
+ import fs from "fs/promises";
5
+ import path from "path";
6
+ import { ConfigSchema } from "./types.js";
7
+ dotenvConfig();
8
+ export class ConfigManager {
9
+ config = null;
10
+ genAI = null;
11
+ configSource = "not_configured";
12
+ isConfigured() {
13
+ return this.config !== null && this.genAI !== null;
14
+ }
15
+ async load() {
16
+ // Priority 1: Environment variable
17
+ const envApiKey = process.env.GEMINI_API_KEY;
18
+ if (envApiKey) {
19
+ try {
20
+ this.config = ConfigSchema.parse({ geminiApiKey: envApiKey });
21
+ this.genAI = new GoogleGenAI({ apiKey: this.config.geminiApiKey });
22
+ this.configSource = "environment";
23
+ return;
24
+ }
25
+ catch {
26
+ // Invalid key, fall through
27
+ }
28
+ }
29
+ // Priority 2: Config file
30
+ try {
31
+ const configPath = path.join(process.cwd(), ".nano-banana-config.json");
32
+ const configData = await fs.readFile(configPath, "utf-8");
33
+ const parsed = JSON.parse(configData);
34
+ this.config = ConfigSchema.parse(parsed);
35
+ this.genAI = new GoogleGenAI({ apiKey: this.config.geminiApiKey });
36
+ this.configSource = "config_file";
37
+ }
38
+ catch {
39
+ this.configSource = "not_configured";
40
+ }
41
+ }
42
+ async setApiKey(apiKey) {
43
+ ConfigSchema.parse({ geminiApiKey: apiKey });
44
+ this.config = { geminiApiKey: apiKey };
45
+ this.genAI = new GoogleGenAI({ apiKey });
46
+ this.configSource = "config_file";
47
+ await this.save();
48
+ }
49
+ async save() {
50
+ if (this.config) {
51
+ const configPath = path.join(process.cwd(), ".nano-banana-config.json");
52
+ await fs.writeFile(configPath, JSON.stringify(this.config, null, 2));
53
+ }
54
+ }
55
+ }
56
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,gBAAgB;AAChB,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,MAAM,IAAI,YAAY,EAAE,MAAM,QAAQ,CAAC;AAChD,OAAO,EAAE,MAAM,aAAa,CAAC;AAC7B,OAAO,IAAI,MAAM,MAAM,CAAC;AAExB,OAAO,EAAU,YAAY,EAAE,MAAM,YAAY,CAAC;AAElD,YAAY,EAAE,CAAC;AAIf,MAAM,OAAO,aAAa;IACxB,MAAM,GAAkB,IAAI,CAAC;IAC7B,KAAK,GAAuB,IAAI,CAAC;IACjC,YAAY,GAAiB,gBAAgB,CAAC;IAE9C,YAAY;QACV,OAAO,IAAI,CAAC,MAAM,KAAK,IAAI,IAAI,IAAI,CAAC,KAAK,KAAK,IAAI,CAAC;IACrD,CAAC;IAED,KAAK,CAAC,IAAI;QACR,mCAAmC;QACnC,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC;QAC7C,IAAI,SAAS,EAAE,CAAC;YACd,IAAI,CAAC;gBACH,IAAI,CAAC,MAAM,GAAG,YAAY,CAAC,KAAK,CAAC,EAAE,YAAY,EAAE,SAAS,EAAE,CAAC,CAAC;gBAC9D,IAAI,CAAC,KAAK,GAAG,IAAI,WAAW,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,YAAY,EAAE,CAAC,CAAC;gBACnE,IAAI,CAAC,YAAY,GAAG,aAAa,CAAC;gBAClC,OAAO;YACT,CAAC;YAAC,MAAM,CAAC;gBACP,4BAA4B;YAC9B,CAAC;QACH,CAAC;QAED,0BAA0B;QAC1B,IAAI,CAAC;YACH,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,0BAA0B,CAAC,CAAC;YACxE,MAAM,UAAU,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;YAC1D,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;YACtC,IAAI,CAAC,MAAM,GAAG,YAAY,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;YACzC,IAAI,CAAC,KAAK,GAAG,IAAI,WAAW,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,YAAY,EAAE,CAAC,CAAC;YACnE,IAAI,CAAC,YAAY,GAAG,aAAa,CAAC;QACpC,CAAC;QAAC,MAAM,CAAC;YACP,IAAI,CAAC,YAAY,GAAG,gBAAgB,CAAC;QACvC,CAAC;IACH,CAAC;IAED,KAAK,CAAC,SAAS,CAAC,MAAc;QAC5B,YAAY,CAAC,KAAK,CAAC,EAAE,YAAY,EAAE,MAAM,EAAE,CAAC,CAAC;QAC7C,IAAI,CAAC,MAAM,GAAG,EAAE,YAAY,EAAE,MAAM,EAAE,CAAC;QACvC,IAAI,CAAC,KAAK,GAAG,IAAI,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC;QACzC,IAAI,CAAC,YAAY,GAAG,aAAa,CAAC;QAClC,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;IACpB,CAAC;IAEO,KAAK,CAAC,IAAI;QAChB,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YAChB,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,0BAA0B,CAAC,CAAC;YACxE,MAAM,EAAE,CAAC,SAAS,CAAC,UAAU,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QACvE,CAAC;IACH,CAAC;CACF"}
@@ -0,0 +1,12 @@
1
+ export declare function getImagesDirectory(): string;
2
+ export declare function getMimeType(filePath: string): string;
3
+ export interface SavedImage {
4
+ filePath: string;
5
+ sizeBytes: number;
6
+ }
7
+ export declare function saveImageFromBase64(base64Data: string, prefix: "generated" | "edited"): Promise<SavedImage>;
8
+ export declare function readImageAsBase64(imagePath: string): Promise<{
9
+ base64: string;
10
+ mimeType: string;
11
+ }>;
12
+ //# sourceMappingURL=image-utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"image-utils.d.ts","sourceRoot":"","sources":["../src/image-utils.ts"],"names":[],"mappings":"AAKA,wBAAgB,kBAAkB,IAAI,MAAM,CAa3C;AAED,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAapD;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,wBAAsB,mBAAmB,CACvC,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,WAAW,GAAG,QAAQ,GAC7B,OAAO,CAAC,UAAU,CAAC,CAarB;AAED,wBAAsB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC;IAClE,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC,CAMD"}
@@ -0,0 +1,48 @@
1
+ // src/image-utils.ts
2
+ import fs from "fs/promises";
3
+ import path from "path";
4
+ import os from "os";
5
+ export function getImagesDirectory() {
6
+ const platform = os.platform();
7
+ if (platform === "win32") {
8
+ return path.join(os.homedir(), "Documents", "nano-banana-images");
9
+ }
10
+ const cwd = process.cwd();
11
+ if (cwd.startsWith("/usr/") || cwd.startsWith("/opt/") || cwd.startsWith("/var/")) {
12
+ return path.join(os.homedir(), "nano-banana-images");
13
+ }
14
+ return path.join(cwd, "generated_imgs");
15
+ }
16
+ export function getMimeType(filePath) {
17
+ const ext = path.extname(filePath).toLowerCase();
18
+ switch (ext) {
19
+ case ".jpg":
20
+ case ".jpeg":
21
+ return "image/jpeg";
22
+ case ".png":
23
+ return "image/png";
24
+ case ".webp":
25
+ return "image/webp";
26
+ default:
27
+ return "image/jpeg";
28
+ }
29
+ }
30
+ export async function saveImageFromBase64(base64Data, prefix) {
31
+ const imagesDir = getImagesDirectory();
32
+ await fs.mkdir(imagesDir, { recursive: true });
33
+ const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
34
+ const randomId = Math.random().toString(36).substring(2, 8);
35
+ const fileName = `${prefix}-${timestamp}-${randomId}.png`;
36
+ const filePath = path.join(imagesDir, fileName);
37
+ const buffer = Buffer.from(base64Data, "base64");
38
+ await fs.writeFile(filePath, buffer);
39
+ return { filePath, sizeBytes: buffer.length };
40
+ }
41
+ export async function readImageAsBase64(imagePath) {
42
+ const buffer = await fs.readFile(imagePath);
43
+ return {
44
+ base64: buffer.toString("base64"),
45
+ mimeType: getMimeType(imagePath),
46
+ };
47
+ }
48
+ //# sourceMappingURL=image-utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"image-utils.js","sourceRoot":"","sources":["../src/image-utils.ts"],"names":[],"mappings":"AAAA,qBAAqB;AACrB,OAAO,EAAE,MAAM,aAAa,CAAC;AAC7B,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,MAAM,IAAI,CAAC;AAEpB,MAAM,UAAU,kBAAkB;IAChC,MAAM,QAAQ,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC;IAE/B,IAAI,QAAQ,KAAK,OAAO,EAAE,CAAC;QACzB,OAAO,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,WAAW,EAAE,oBAAoB,CAAC,CAAC;IACpE,CAAC;IAED,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IAC1B,IAAI,GAAG,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,GAAG,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,GAAG,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;QAClF,OAAO,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,oBAAoB,CAAC,CAAC;IACvD,CAAC;IAED,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,gBAAgB,CAAC,CAAC;AAC1C,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,QAAgB;IAC1C,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,WAAW,EAAE,CAAC;IACjD,QAAQ,GAAG,EAAE,CAAC;QACZ,KAAK,MAAM,CAAC;QACZ,KAAK,OAAO;YACV,OAAO,YAAY,CAAC;QACtB,KAAK,MAAM;YACT,OAAO,WAAW,CAAC;QACrB,KAAK,OAAO;YACV,OAAO,YAAY,CAAC;QACtB;YACE,OAAO,YAAY,CAAC;IACxB,CAAC;AACH,CAAC;AAOD,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,UAAkB,EAClB,MAA8B;IAE9B,MAAM,SAAS,GAAG,kBAAkB,EAAE,CAAC;IACvC,MAAM,EAAE,CAAC,KAAK,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAE/C,MAAM,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;IACjE,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAC5D,MAAM,QAAQ,GAAG,GAAG,MAAM,IAAI,SAAS,IAAI,QAAQ,MAAM,CAAC;IAC1D,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;IAEhD,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;IACjD,MAAM,EAAE,CAAC,SAAS,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IAErC,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC;AAChD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,SAAiB;IAIvD,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;IAC5C,OAAO;QACL,MAAM,EAAE,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC;QACjC,QAAQ,EAAE,WAAW,CAAC,SAAS,CAAC;KACjC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
package/dist/index.js ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env node
2
+ import { NanoBananaMCP } from "./server.js";
3
+ const server = new NanoBananaMCP();
4
+ server.run().catch(console.error);
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C,MAAM,MAAM,GAAG,IAAI,aAAa,EAAE,CAAC;AACnC,MAAM,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC"}
@@ -0,0 +1,11 @@
1
+ export declare class NanoBananaMCP {
2
+ private server;
3
+ private configManager;
4
+ private chatSession;
5
+ private lastImagePath;
6
+ constructor();
7
+ private setupHandlers;
8
+ private routeToolCall;
9
+ run(): Promise<void>;
10
+ }
11
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAqBA,qBAAa,aAAa;IACxB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,aAAa,CAAgB;IACrC,OAAO,CAAC,WAAW,CAAmC;IACtD,OAAO,CAAC,aAAa,CAAuB;;IAW5C,OAAO,CAAC,aAAa;YA2BP,aAAa;IAuDrB,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;CAK3B"}