image-gen-mcp

image-gen-mcp

Enables generating images from text prompts using Google Gemini's native image models, with configurable aspect ratios and resolution, returning images inline or as signed URLs for larger outputs.

Category
Visit Server

README

image-gen-mcp

An MCP server that generates images with Google's Gemini native image models ("Nano Banana"), served over Streamable HTTP.

One tool, generate_image. No state anywhere, and no auth of its own — in production it runs as a backend behind mcp-oauth-proxy, deployed by cloudrun-mcp-deployment.

DESIGN.md explains why it is built this way; this file explains how to run it.

Quick start (local, token auth)

uv sync
export GEMINI_API_KEY="…"                       # from Google AI Studio
export IMAGE_MCP_TOKEN="$(openssl rand -base64 32)"
uv run python -m image_gen_mcp

Point a client at http://127.0.0.1:8080/mcp with Authorization: Bearer $IMAGE_MCP_TOKEN. For Claude Code:

claude mcp add --transport http image-gen http://127.0.0.1:8080/mcp \
  --header "Authorization: Bearer $IMAGE_MCP_TOKEN"

The tool

generate_image(prompt, aspect_ratio="1:1", image_size="1K", model=None)
Parameter Values
prompt free text, up to IMAGE_MCP_MAX_PROMPT characters
aspect_ratio 1:1 16:9 9:16 4:3 3:4 3:2 2:3 21:9 4:5 5:4
image_size 1K 2K 4K — 2K/4K need a capable model and a bucket
model optional; must be in IMAGE_MCP_ALLOWED_MODELS

Images at or under IMAGE_MCP_INLINE_MAX_BYTES (default 1.5 MB) come back inline and render in the chat. Larger ones are uploaded to Cloud Storage and returned as a signed URL. Structured output always reports the true pixel dimensions, the model used, and which delivery route was taken.

Model / resolution support

Model 1K 2K 4K
gemini-2.5-flash-image (default)
gemini-3-pro-image-preview / gemini-3-pro-image
gemini-3.1-flash-lite-image

Unsupported combinations are rejected at the tool boundary, in milliseconds, with a message naming what is supported — rather than after a 30-second round trip.

Configuration

Everything comes from the environment. See .env.example for the annotated list; the essentials:

Variable Required Purpose
GEMINI_API_KEY yes Google AI Studio key
IMAGE_MCP_TOKEN unless proxied static bearer token
IMAGE_MCP_MODEL default model
IMAGE_MCP_ALLOWED_MODELS models a caller may select
IMAGE_MCP_TRUST_PROXY_HEADERS behind proxy take identity from X-Auth-*
ALLOWED_EMAILS optional narrowing of the proxy's allowlist
IMAGE_MCP_GCS_BUCKET for 2K/4K bucket for oversized images

The server refuses to start rather than run misconfigured: no API key, and no auth boundary at all (neither a static token nor proxy mode) are both startup failures.

Auth

Two shapes, and the server refuses to start in neither.

Behind the proxy (production). Set IMAGE_MCP_TRUST_PROXY_HEADERS=1. The proxy authenticates the user against Google, enforces its allowlist, strips the client's Authorization header, and forwards the identity as X-Auth-Email / X-Auth-Subject / X-Auth-Scope. This server reads those and requires no token of its own — a request without X-Auth-Email gets a 401.

Trusting headers is safe only because nothing else can reach the process: in the Cloud Run multi-container layout the backend declares no ingress port, so the only things that can open a socket to it are the proxy in the same instance and the startup prober. Never enable this on a routable port.

ALLOWED_EMAILS is optional here and narrows the proxy's allowlist — useful when the proxy admits a whole domain but image generation should cost money for only a few people. Unset means "whoever the proxy admitted".

Token (local, Claude Code). Leave IMAGE_MCP_TRUST_PROXY_HEADERS unset and set IMAGE_MCP_TOKEN. Callers send Authorization: Bearer <token>; the X-Auth-* headers are ignored entirely, since without the proxy they are just untrusted request data.

The server does not terminate OAuth and has no /authorize, /token or /register endpoints. Setting GOOGLE_OAUTH_CLIENT_ID or _SECRET is a startup error rather than a silent no-op — those belong on the proxy.

Building the image

.github/workflows/build.yml runs the tests, builds the image, and publishes it to GitHub Container Registry. It does not deploy — deployment is handled by a separate workflow or repository.

Event Test Build Push
pull request
push to main latest, sha-<full-sha>
tag v* 1.2.3, 1.2, sha-<full-sha>

Published as ghcr.io/ramzpat/image-gen-mcp. Nothing needs configuring: the workflow authenticates with the built-in GITHUB_TOKEN.

Consuming it from a deploy workflow

Deploy by digest, not by tag. A pull-through cache in front of a mutable tag like :latest will happily serve a previous image; a digest cannot go stale. Each run prints the digest to its job summary, and the workflow is callable if you want to build and deploy in one pipeline:

jobs:
  build:
    uses: ramzpat/image-gen-mcp/.github/workflows/build.yml@main
  deploy:
    needs: build
    runs-on: ubuntu-latest
    steps:
      - run: echo "deploying ${{ needs.build.outputs.image }}@${{ needs.build.outputs.digest }}"

From a separate repository, resolve the digest at deploy time instead:

DIGEST=$(docker buildx imagetools inspect \
  ghcr.io/ramzpat/image-gen-mcp:latest --format '{{.Manifest.Digest}}')

The GHCR package is private by default. A deploy job in another repository needs either a PAT with read:packages, or the package set to public under its GitHub package settings.

Deployment

Deployed by cloudrun-mcp-deployment (.github/workflows/deploy-image-gen-mcp.yml), which runs this image as the backend container of a Cloud Run multi-container service with mcp-oauth-proxy in front. That repo owns the GCP project, region, allowlist, and secrets; this one only publishes the image.

What that deployment sets on this container:

PORT / HOST 8000 / 0.0.0.0 (set by the shared deploy action)
IMAGE_MCP_TRUST_PROXY_HEADERS 1
GEMINI_API_KEY from the service's GitHub Environment secret
entrypoint /app/.venv/bin/python -m image_gen_mcp

HOST=0.0.0.0 rather than loopback is required, not a leak: Cloud Run's startup prober runs outside the container's network namespace and cannot reach a loopback-only socket. Only the container declaring --port (the proxy) receives ingress, so the backend stays unreachable from outside the instance.

Cloud Storage for 2K/4K

gcloud storage buckets create gs://BUCKET --uniform-bucket-level-access
gcloud storage buckets update gs://BUCKET \
  --lifecycle-file=<(echo '{"rule":[{"action":{"type":"Delete"},"condition":{"age":30}}]}')

# The runtime service account signs URLs through the IAM Credentials API,
# because it has no private key file. It needs this role *on itself*:
gcloud iam service-accounts add-iam-policy-binding RUNTIME_SA \
  --member="serviceAccount:RUNTIME_SA" --role=roles/iam.serviceAccountTokenCreator
gcloud storage buckets add-iam-policy-binding gs://BUCKET \
  --member="serviceAccount:RUNTIME_SA" --role=roles/storage.objectAdmin

Omitting the serviceAccountTokenCreator binding is the most common way signed URLs ship broken.

Tests

uv run pytest -q

57 tests: startup guards, identity resolution in both shapes (proxy headers, static token, and each ignoring the other's credential), tool validation and delivery, and end-to-end smoke tests driving a real MCP client over real HTTP against uvicorn — in token mode and in the proxied production shape.

Cost

Every allowlisted user draws on one API key. The controls, in order of how much they actually help: --max-instances, IMAGE_MCP_MAX_CONCURRENCY, a GCP billing budget alert, and IMAGE_MCP_RATE_PER_HOUR. The rate limit is counted per instance, so its true ceiling is IMAGE_MCP_RATE_PER_HOUR × --max-instances.

Recommended Servers

playwright-mcp

playwright-mcp

A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.

Official
Featured
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.

Official
Featured
Local
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.

Official
Featured
Local
TypeScript
VeyraX MCP

VeyraX MCP

Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.

Official
Featured
Local
graphlit-mcp-server

graphlit-mcp-server

The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.

Official
Featured
TypeScript
Kagi MCP Server

Kagi MCP Server

An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.

Official
Featured
Python
E2B

E2B

Using MCP to run code via e2b.

Official
Featured
Neon Database

Neon Database

MCP server for interacting with Neon Management API and databases

Official
Featured
Exa Search

Exa Search

A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.

Official
Featured
Qdrant Server

Qdrant Server

This repository is an example of how to create a MCP server for Qdrant, a vector search engine.

Official
Featured