Skip to main content
Glama
nano-banana-pro.js10.8 kB
/** * Nano Banana Pro Tool for Gemini MCP Server. * Uses Gemini 3 Pro Image (Nano Banana Pro) via OpenRouter for professional-grade image generation. * Supports up to 14 reference images, 4K resolution, advanced text rendering, and multi-character consistency. * * @author Claude + Rob */ const crypto = require('crypto'); const path = require('path'); const fs = require('fs'); const BaseTool = require('./base-tool'); const { log } = require('../utils/logger'); const { ensureDirectoryExists, readFileAsBuffer, validateFileSize, getMimeType } = require('../utils/file-utils'); const { validateNonEmptyString, validateString } = require('../utils/validation'); const config = require('../config'); const openRouterService = require('../openrouter/openrouter-service'); class NanoBananaProTool extends BaseTool { constructor(intelligenceSystem, geminiService) { super( 'gemini-nano-banana-pro', 'Generate professional images with Nano Banana Pro (Gemini 3 Pro Image): 4K resolution, up to 14 reference images, advanced text rendering, character consistency, and studio-grade controls', { type: 'object', properties: { prompt: { type: 'string', description: 'Text description of the desired image or editing instruction', }, mode: { type: 'string', enum: ['fusion', 'consistency', 'targeted_edit', 'template', 'standard'], description: 'Generation mode: fusion (blend up to 14 images), consistency (maintain character/style for up to 5 characters), targeted_edit (precise localized edits), template (follow layout), standard (basic generation)', }, resolution: { type: 'string', enum: ['1k', '2k', '4k'], description: 'Output resolution: 1k (1024px), 2k (2048px), or 4k (4096px). Higher resolutions cost more.', }, aspect_ratio: { type: 'string', enum: ['1:1', '2:3', '3:2', '3:4', '4:3', '4:5', '5:4', '9:16', '16:9', '21:9'], description: 'Aspect ratio for the generated image', }, reference_images: { type: 'array', items: { type: 'string', }, description: 'Optional array of file paths to reference images (up to 14 for Nano Banana Pro)', }, context: { type: 'string', description: 'Optional context for intelligent enhancement (e.g., "professional", "artistic", "infographic")', }, }, required: ['prompt'], }, intelligenceSystem, geminiService, ); } /** * Executes the Nano Banana Pro image generation tool. * @param {Object} args - The arguments for the tool. * @param {string} args.prompt - The text description or editing instruction. * @param {string} [args.mode='standard'] - The generation mode. * @param {string} [args.resolution='1k'] - Output resolution. * @param {string} [args.aspect_ratio='1:1'] - Aspect ratio. * @param {string[]} [args.reference_images] - Array of file paths to reference images. * @param {string} [args.context] - Optional context for intelligent enhancement. * @returns {Promise<Object>} A promise that resolves to the tool's result. */ async execute(args) { const prompt = validateNonEmptyString(args.prompt, 'prompt'); const mode = args.mode || 'standard'; const resolution = args.resolution || '1k'; const aspectRatio = args.aspect_ratio || '1:1'; const referenceImagePaths = args.reference_images || []; const context = args.context ? validateString(args.context, 'context') : null; log(`Nano Banana Pro: mode="${mode}", resolution="${resolution}", aspect_ratio="${aspectRatio}", prompt="${prompt}"`, this.name); try { // Validate reference images count if (referenceImagePaths.length > 14) { throw new Error(`Nano Banana Pro supports up to 14 reference images, got ${referenceImagePaths.length}`); } // Process reference images const referenceImages = []; if (referenceImagePaths.length > 0) { log(`Processing ${referenceImagePaths.length} reference images`, this.name); for (let i = 0; i < referenceImagePaths.length; i++) { const imagePath = referenceImagePaths[i]; log(`Processing reference image ${i + 1}/${referenceImagePaths.length}: ${imagePath}`, this.name); try { if (!path.isAbsolute(imagePath)) { throw new Error(`File path must be absolute, got relative path: ${imagePath}`); } if (!fs.existsSync(imagePath)) { throw new Error(`Reference image file not found: ${imagePath}`); } validateFileSize(imagePath, config.MAX_IMAGE_SIZE_MB); const imageBuffer = readFileAsBuffer(imagePath); const mimeType = getMimeType(imagePath, config.SUPPORTED_IMAGE_MIMES); referenceImages.push({ data: imageBuffer.toString('base64'), mimeType, }); log(`✓ Loaded reference image ${i + 1}: ${imagePath} (${(imageBuffer.length / 1024).toFixed(2)}KB)`, this.name); } catch (fileError) { throw new Error(`Reference Image Error: Failed to process image ${i + 1} (${imagePath}): ${fileError.message}`); } } } // Validate mode requirements if (['fusion', 'consistency', 'template'].includes(mode) && referenceImages.length === 0) { throw new Error(`Mode "${mode}" requires at least one reference image`); } if (mode === 'fusion' && referenceImages.length < 2) { throw new Error('Fusion mode requires at least 2 reference images'); } // Apply intelligent enhancement let enhancedPrompt = prompt; if (this.intelligenceSystem.initialized) { try { const contextForEnhancement = context || mode; enhancedPrompt = await this.intelligenceSystem.enhancePrompt(prompt, contextForEnhancement, this.name); log('Applied Tool Intelligence enhancement', this.name); } catch (err) { log(`Tool Intelligence enhancement failed: ${err.message}`, this.name); } } // Check if OpenRouter is available if (!openRouterService.isServiceAvailable()) { throw new Error('OpenRouter service is not available. Nano Banana Pro requires OpenRouter.'); } // Generate image using Nano Banana Pro log('Generating image with Nano Banana Pro via OpenRouter', this.name); const imageData = await openRouterService.generateNanaBananaProImage( enhancedPrompt, referenceImages, { mode, resolution, aspect_ratio: aspectRatio, } ); if (imageData) { log('Successfully generated Nano Banana Pro image', this.name); ensureDirectoryExists(config.OUTPUT_DIR, this.name); const timestamp = Date.now(); const hash = crypto.createHash('md5').update(prompt + mode + resolution).digest('hex').substring(0, 8); const imageName = `nanobananapro-${mode}-${resolution}-${hash}-${timestamp}.png`; const imagePath = path.join(config.OUTPUT_DIR, imageName); fs.writeFileSync(imagePath, Buffer.from(imageData, 'base64')); log(`Image saved to: ${imagePath}`, this.name); // Learn from interaction if (this.intelligenceSystem.initialized) { try { const resultDescription = `Nano Banana Pro image generated (${mode}, ${resolution}): ${imagePath}`; await this.intelligenceSystem.learnFromInteraction( prompt, enhancedPrompt, resultDescription, context || mode, this.name ); } catch (err) { log(`Tool Intelligence learning failed: ${err.message}`, this.name); } } // Build response let finalResponse = `✓ **Nano Banana Pro** image successfully generated\n\n`; finalResponse += `**Mode:** ${mode}\n`; finalResponse += `**Resolution:** ${resolution}\n`; finalResponse += `**Aspect Ratio:** ${aspectRatio}\n`; finalResponse += `**Prompt:** "${prompt}"\n`; finalResponse += `**Output:** ${imagePath}`; if (referenceImages.length > 0) { finalResponse += `\n**Reference Images:** ${referenceImages.length} image(s)`; } // Cost estimation const costEstimate = this.estimateCost(resolution); finalResponse += `\n\n💰 **Estimated Cost:** ~$${costEstimate.toFixed(3)}`; // Mode-specific details switch (mode) { case 'fusion': finalResponse += `\n\n**Fusion Details:** Blended ${referenceImages.length} images with advanced reasoning`; break; case 'consistency': finalResponse += `\n\n**Consistency Details:** Maintained character/style (supports up to 5 characters)`; break; case 'targeted_edit': finalResponse += `\n\n**Edit Details:** Applied precise localized modifications`; break; case 'template': finalResponse += `\n\n**Template Details:** Followed layout and structure from reference`; break; } // Nano Banana Pro capabilities note finalResponse += `\n\n---\n_Powered by Nano Banana Pro (Gemini 3 Pro Image) via OpenRouter_`; return { content: [ { type: 'text', text: finalResponse, }, ], }; } throw new Error('No image data returned from Nano Banana Pro'); } catch (error) { log(`Nano Banana Pro error: ${error.message}`, this.name); if (error.message.includes('Reference Image Error:')) { throw new Error(`${error.message}\n\nSupported formats: ${Object.keys(config.SUPPORTED_IMAGE_MIMES).join(', ')}`); } else if (error.message.includes('Mode') && error.message.includes('requires')) { throw new Error(`${error.message}\n\nNote: Fusion needs 2+ images, consistency/template need 1+`); } else { throw new Error(`Nano Banana Pro failed: ${error.message}`); } } } /** * Estimates the cost based on resolution. * Pricing: $0.139 for 1k/2k, $0.24 for 4k * @param {string} resolution - The output resolution * @returns {number} Estimated cost in USD */ estimateCost(resolution) { switch (resolution) { case '4k': return 0.24; case '2k': case '1k': default: return 0.139; } } } module.exports = NanoBananaProTool;

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Garblesnarff/gemini-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server