nano-banana-mcp
Generates AI images via Google's Flow platform (Nano Banana), supporting exact size cropping, reference images, variant batches, and zero-cost generation with a signed-in Google account.
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@nano-banana-mcpGenerate a 1200x630 header image of a mountain landscape at sunset"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
generate_image(prompt: "an orange fox on a white background", size: "1200x630")
-> images/an-orange-fox-on-a-white-background.jpg 1200x630 0 creditsEvery image in this README was generated by this server. The banner above, the crops below, and the logo renders further down. Nothing was touched up by hand.
Why this one is different
Every other image MCP server bills you. You register for an API key, attach a credit card, and pay per image — a few cents each, which quietly becomes real money once a model is iterating on a design and throwing away nine attempts out of ten.
This one doesn't, because it isn't calling a paid API at all. It drives Nano Banana, the image model inside Google Flow, through the web app you already have access to. Flow charges credits for video. Images cost zero.
API-based image MCPs | nano-banana-mcp | |
API key | Required | None |
Billing account | Required | None |
Cost per image | Cents each, and it adds up | $0.00 |
Setup | Register, key, billing, secret | Sign into Google in a Chrome window |
Exact pixel sizes | Rarely | Yes |
Reference images | Sometimes | Yes |
The trade-off is honest and worth stating: it needs a real Chrome window that you are signed into, so it can't run headless or in CI. That's the price of not having a bill.
Related MCP server: NanoBananaPro
What it solves
Flow generates in five fixed aspect ratios: 16:9, 4:3, 1:1, 3:4 and 9:16. Real design work almost never lands on one of them — an Open Graph card is 1200×630, a repo banner is 1456×180, an avatar is 400×400.
This server generates at the closest native ratio and crops to the exact size, using saliency detection so the crop
doesn't decapitate your subject. Ask for 1200x630, get a 1200×630 file.
What "exact size" does and doesn't mean. The model is not generating at your dimensions — Flow always returns its own native resolution, around 1 megapixel (1376×768 for 16:9, 1024×1024 for 1:1). The exact size is produced here, locally, by cropping and scaling that result. The file you get really is 1200×630; the pixels in it came from a 1376×768 render.
That's a genuine convenience when you're going down from native, which covers most web work. Going up it will still hand you the file you asked for — request 3000×3000 and you get 3000×3000 — but those extra pixels are interpolated, not generated. There is no more detail in them. The server prints a warning when this happens; if you need real resolution beyond native, upscale deliberately with a tool built for it.
One generation, three sizes. Same source image, cropped three ways — note how the crop follows the subject instead of blindly taking the centre:
|
|
1456x180 — repo banner
Reference images
Give it an image and the prompt stops describing what to create and starts describing what to change. Feed it a flat logo and ask for it letterpressed into cotton paper, or cast as a matte signage object:
Left: the source vector, handed in as a reference. Centre and right: two generations from it, four variants each, 0 credits, about a minute per batch.
generate_image({
prompt: "this logo letterpressed into thick warm off-white cotton paper, raking side light",
reference_images: ["assets/logo.png"],
count: 4
})
// iterating on the same reference? don't re-upload it
generate_image({
prompt: "same, but with a deeper impression and visible paper tooth",
reference_library_names: ["logo.png"],
count: 4
})Flow doesn't take a file straight into the composer: it uploads to the project library first, and you pick it from there. Both steps are handled for you, including the confirm click that actually attaches it. Any reference left over from a previous turn is cleared first — a forgotten one silently changes the image, and the result gets blamed on the prompt.
How it works
Flow has no public API. Its internal generation call is signed with a reCAPTCHA Enterprise token minted by the page's own JavaScript, so it cannot be replayed from outside the browser — and this project does not try to. That single constraint shapes the whole design.
So it does what a person would do: type into the composer and press Enter. What's different is how it reads the result.
It intercepts the page's own network response instead of watching the library for something new to show up. That response already carries the media id, the true dimensions and a signed URL, so there's no polling, no guessing which thumbnail is yours, and no ambiguity when several generations are in flight. The only fragile part left is typing the prompt.
One request, four responses. Ask for four variants and Flow doesn't return an array — it sends four separate HTTP responses, staggered by a couple of seconds. Waiting for "the next response" quietly drops three of them and looks exactly like an account limit. The collector listens to the whole stream and closes on whichever comes first: all expected images, a 20-second silence, or the hard timeout. It never discards what already arrived.
UI anchors are Material Symbols ligature names (crop_16_9, image, add_2) and numeric labels (16:9, x4).
Those are identifiers, not translatable text, so it behaves the same whether the interface is in English, Spanish or
Japanese.
The cost gate closes before sending. The server reads the cost Flow itself computes in its settings panel and
aborts if it exceeds FLOW_MAX_COST, which defaults to 0. The refusal happens while refusing is still free. If the
number can't be read, it doesn't send either — it won't guess.
Requirements
Node.js 20 or newer
Google Chrome
A Google account with access to Flow
That's the whole list. No API key, no cloud project, no billing account, no secret to rotate.
Install
git clone https://github.com/frannkurt/nano-banana-mcp.git
cd nano-banana-mcp
npm install
npm run buildSetup
1. Launch Chrome with remote debugging
Use a profile separate from your everyday one.
Windows
"C:\Program Files\Google\Chrome\Application\chrome.exe" --remote-debugging-port=9222 --user-data-dir="%USERPROFILE%\.nano-banana-mcp\chrome" https://labs.google/fx/tools/flowmacOS
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" --remote-debugging-port=9222 --user-data-dir="$HOME/.nano-banana-mcp/chrome" https://labs.google/fx/tools/flowLinux
google-chrome --remote-debugging-port=9222 --user-data-dir="$HOME/.nano-banana-mcp/chrome" https://labs.google/fx/tools/flow2. Sign in and open a project
In that window, sign in with your Google account and open a Flow project. The URL must end up at
labs.google/fx/tools/flow/project/<id>.
Without an open project there is no composer, and without a composer nothing can be generated.
3. Register the server
In Claude Code:
claude mcp add nano-banana --env FLOW_CDP_URL=http://127.0.0.1:9222 --env FLOW_OUTPUT_DIR=./images -- node /path/to/nano-banana-mcp/dist/index.jsOr by hand, in your MCP client's config:
{
"mcpServers": {
"nano-banana": {
"command": "node",
"args": ["/path/to/nano-banana-mcp/dist/index.js"],
"env": {
"FLOW_CDP_URL": "http://127.0.0.1:9222",
"FLOW_OUTPUT_DIR": "./images",
"FLOW_MAX_COST": "0"
}
}
}
}4. Check it
node scripts/doctor.mjsThat checks the browser, session, project, composer and balance in order, and stops at the first thing that's wrong with instructions for fixing it. Once it's clean, try a real generation:
node scripts/smoke.mjs "an orange fox on a white background" 1200x630Tools
flow_status
Connection state: session, account, open project, credit balance. Start here when something fails.
generate_image
Parameter | Type | Default | What it does |
| string | — | Image description, in any language |
| string | native | Exact output size, |
|
| derived from | Native ratio to generate at |
| 1–4 | 1 | How many variants |
| string[] | — | Local paths to use as references; uploaded and attached for you |
| string[] | — | Files already in the project library, attached without re-uploading |
| string |
| Destination folder |
| string | derived from prompt | Base filename |
|
|
| Output format |
|
|
|
|
Returns the saved paths, each media id, and a thumbnail of every result — so the model can see what came out and decide whether it's worth another try.
download_image
Fetches an existing image by media id, with optional cropping. Useful for recovering something generated earlier, or pulling several sizes out of the same original.
Configuration
Variable | Default | What it does |
|
| Chrome's debugging endpoint |
|
| Where images are saved |
|
| Credit ceiling per generation |
|
| How long to wait for Flow to answer |
| system locale, else | Language of this server's messages: |
Languages
Two different languages meet in this project, and it's worth not confusing them.
Flow's interface language is whatever your Google account is set to, and this server never depends on it. It
anchors on Material Symbols ligature names (crop_16_9, add_2, image) and numeric labels (16:9, x4) — those
are identifiers, not copy, so they read the same in every locale. It never matches translatable text like "Add to
prompt". Tested against a Spanish interface; the anchors are language-independent by construction.
The one place that used to depend on it was reading the cost. That's now taken from the panel's structure — the leaf
<a> element holding the number — instead of matching the word next to it. It matters because the cost gate refuses
to send when it can't read the number, so a German user seeing "0 Punkte" would have been blocked from generating
anything at all. Safe, but useless.
This server's own messages — errors, warnings, status, and the MCP tool descriptions your model reads — come in
English and Spanish. It picks from FLOW_LANG, falling back to your system locale, defaulting to English.
FLOW_LANG=es node dist/index.jsCode comments stay in Spanish. That's a choice about how this codebase is written, not something a user ever sees.
Generating in parallel
The composer is a single element per tab, so two generations in the same tab overwrite each other's prompt. Give each worker its own tab and they don't collide — each waits for its own network response, so there's no question which image belongs to whom.
ensureFlowTabs(n) clones the project tab as many times as you need. It's a library-level API rather than an MCP tool,
because the number of workers is a decision for the calling script:
import { ensureFlowTabs } from "nano-banana-mcp/dist/browser.js";
import { generateImages } from "nano-banana-mcp/dist/generate.js";
const tabs = await ensureFlowTabs(4);
await Promise.all(prompts.map((prompt, i) =>
generateImages({ prompt, aspect: "16:9", count: 4, page: tabs[i % tabs.length].page })
));Four tabs × four variants is sixteen images per cycle. In practice that's roughly 40 images in five minutes.
On cost
Images in Flow cost 0 credits. Video costs, and it costs a lot.
The gate described above is what keeps that true by accident rather than by trust: the server reads Flow's own quoted cost and refuses to send anything above the ceiling.
Roadmap
Video and scene generation are in development. Not shipped yet — today this server generates images only.
Video is where the cost gate stops being a formality, so it will land behind an explicit, non-zero FLOW_MAX_COST
and a per-call confirmation. Nothing that spends credits will ever run because a default let it.
Privacy and credentials
It never handles a credential. It attaches to a session you opened yourself.
The balance is read with a token that is fetched and used inside the tab. That token never crosses into this process, is never written to disk, and is never logged.
Nothing is sent anywhere except Google Flow.
Troubleshooting
Run node scripts/doctor.mjs first — it checks the whole chain in order and stops at the first thing that's wrong.
The messages below are the English ones; with FLOW_LANG=es you'll see the Spanish equivalents.
"Couldn't connect to Chrome at …" — Chrome isn't running with --remote-debugging-port=9222, or you launched it
without its own --user-data-dir and it attached to an existing instance. Close every window of that profile and
relaunch with the command above.
"No labs.google tab is open in that Chrome" — open Flow in that Chrome window.
"Couldn't find the prompt composer on the page" — you're on the project list, not inside a project. The URL must
contain /project/.
"I couldn't read what this generation would cost, so I'm not sending it" — Flow's interface changed. The error includes the text it did read; open an issue pasting it and it gets fixed in one place. Note this is a refusal, not a crash: nothing was sent and nothing was spent.
"Couldn't find X in the library picker" — the reference file isn't in this project's library, or the name doesn't match. Check the exact filename as it was uploaded.
"Selected X in the library but it didn't attach to the composer" — the picker's confirm button moved. Open an issue with the Flow's interface changed template.
It generated but the crop is wrong — try fit: "contain", or pass an explicit aspect closer to your final size
instead of letting it be derived.
Limitations
Images only. Video and scenes are in development, not available yet.
Needs a visible, signed-in Chrome window. It does not work headless or in CI.
It depends on Flow's interface to type the prompt. Google can change it; when they do, the submit step breaks and needs adjusting.
Not a Google product. Not endorsed by or affiliated with Google.
Contributing
Yes, please — see CONTRIBUTING.md. The most valuable contribution is a fix for a Flow UI change, and that guide explains how to diagnose one properly instead of guessing at selectors.
Also welcome: setup reports from macOS and Linux (this was built on Windows), and testing against interfaces in languages other than Spanish and English.
By participating you agree to the Code of Conduct. Security issues go here, privately.
License
Apache-2.0. See LICENSE and NOTICE.
Not a Google product. Not endorsed by or affiliated with Google.
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- AlicenseAqualityFmaintenanceEnables AI assistants to generate images from text prompts and transform existing images using Google Gemini's nano banana model through the Nanana AI service. Supports both text-to-image generation and image-to-image transformation capabilities.22610MIT
- Alicense-qualityCmaintenanceUse Nano Banana Pro to generate image from text prompt and edit image3Apache 2.0
- AlicenseAqualityCmaintenanceLocal MCP server for generating image assets with Google Gemini (Nano Banana 2 / Pro). Supports transparent PNG/WebP output, exact resizing/cropping, up to 14 reference images, and Google Search grounding.1378MIT
- AlicenseAqualityBmaintenanceControls Google Flow for image and video generation from an AI agent. Enables generating images with models like Imagen 4, creating videos, managing characters and scenes via browser automation.41715830MIT
Related MCP Connectors
Generate logos, social posts, app screenshots, comic panels & visual-novel assets from prompts.
Generate PWA icon sets and iOS splash screens from a text prompt or an existing image.
MCP server for NanoBanana AI image generation and editing
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
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/frannkurt/nano-banana-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server