Skip to main content
Glama

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"

Related MCP server: Imagen MCP Server

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.

F
license - not found
Not graded
quality - not tested
B
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

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

View all related MCP servers

Related MCP Connectors

  • Generate images with any major model — one API key, one prepaid balance, one MCP.

  • Generate images, video & speech with Nano Banana, Veo, Omni and Gemini TTS. Pay as you go.

  • Generate logos, social posts, app screenshots, comic panels & visual-novel assets from prompts.

View all MCP Connectors

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/ramzpat/image-gen-mcp'

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