Skip to main content
Glama

wrmax-criativo

WRMax image generation and editing pipeline. Claude Code is the brain; this repo is the hand.

The code knows nothing about marketing — it receives parameters and returns a file. Whoever decides format, angle, and prompt is Claude, which then looks at the generated piece and decides whether to accept it or redo it. It's this closed loop that characterizes the orchestration.

The code, comments, and messages are in English. The documentation and team conversation are in Portuguese.


Setup (5 minutes)

npm install
export OPENAI_API_KEY="sua-chave"      # https://platform.openai.com/api-keys

Important: a ChatGPT Pro or Gemini app subscription does not grant API access. They are separate charges. You need an API key with active billing.

Engine B (not yet implemented):

export IMAGE_PROVIDER=gemini
export GEMINI_API_KEY="sua-chave"

Related MCP server: MCP OpenAI Image Generation Server

Structure

Each folder has one responsibility, and no file accumulates two.

bin/                      entradas executáveis
  cli.js                    CLI
  mcp-server.js             servidor MCP (só escolhe o transporte)

src/
  bootstrap/              carga do .env e resolução de caminhos
  config/                 ÚNICO ponto que lê process.env; tabelas de modelo,
                          formato e qualidade
  brands/                 brand kit, compliance e montagem do prompt
  media/                  entrada, redução e saída de imagem (Drive, download,
                          arquivo local, preview, upload)
  providers/              motores de imagem, por registro
  core/                   regra de negócio: artwork-service, artifact-store,
                          delivery
  mcp/                    servidor MCP, tools e transportes
  http/                   app Express, middleware, rotas e views
  auth/                   OAuth com Google
  cli/                    args, ajuda e orquestração do CLI

test/                     node --test, sem chave e sem custo
scripts/                  smoke — gasta crédito ou precisa de rede viva
brand/                    um JSON por cliente
out/                      saída local (só com PERSIST_OUTPUT=true)

The central design: src/core/artwork-service.js doesn't know what MCP or CLI is. It receives a simple request and returns a simple result. Whoever formats the content block is src/mcp/tool-result.js; whoever writes JSON to stdout is src/cli/run.js. That's why both frontends share a single path.

Every dependency (config, artifact store, brand directory) is injected, not imported as a singleton — that's what makes it possible to test the route, the tool, and the service without touching the environment.


Usage

Generate from scratch:

node bin/cli.js --brand forno-paulista --format feed \
  --prompt "Studio product shot of a rustic pizza on a wooden board, steam rising"

Edit a real customer photo (background swap preserving the product):

node bin/cli.js --brand forno-paulista --format square \
  --ref fotos/produto.jpg \
  --prompt "Change only the background to a clean warm studio gradient. Keep the product, its label and the lighting on it exactly unchanged."

Cheap draft before spending on the final:

node bin/cli.js --quality draft --prompt "..."

Always draft before final. It costs a fraction and avoids an expensive redo.


MCP Server

npm run mcp          # stdio — é o que o Claude Code fala
npm run mcp:http     # Streamable HTTP em :8787/mcp — é o que conector remoto exige

Exposed tools: list_brands, generate_image, edit_image.

There is no tool that lists, searches, or browses images on the server, and that's intentional: whoever picks the file is the user. A search tool would turn an injected prompt into a customer photo into an environment scan.

Transport, authentication, and full-resolution destination details are in CLAUDE.md.


How Claude Code uses the CLI

The command prints JSON to stdout and logs to stderr. That's intentional: Claude runs, reads the JSON, opens the PNG, evaluates, and chains the next call. No human in the middle of each iteration.

{"ok":true,"file":"out/1755777.png","seconds":6.2,"aspectRatio":"4:5"}

Exit codes: 0 success · 1 technical failure · 2 blocked by compliance — 2 exists so a hook can distinguish the two cases.


Compliance

brand/*.json has a forbidden_terms array. assertPromptAllowed() runs before the call and blocks — it saves credits and, more importantly, doesn't depend on the model obeying instructions.

{
  "name": "Forno Paulista",
  "visual": {
    "style": "appetizing food photography, rustic warmth, artisanal",
    "colors": ["wood brown", "tomato red", "warm cream"],
    "lighting": "warm golden light, natural window light",
    "avoid": ["cold blue tones", "plastic-looking food"]
  },
  "forbidden_terms": [],
  "compliance_reason": ""
}

Brand

Blocking

cliente-medico

patient, before/after, body, procedure result — CFM 2.336/2023

Quick guardrail test, without a key and without cost:

node bin/cli.js --brand cliente-medico --prompt "before and after of a patient"
# x BLOCKED by compliance rules for "Cliente médico (template CFM)"

Tests

npm test          # 110 testes, sem chave de API, sem rede externa, sem custo

Covers: compliance, brand kit, config, artifact store, Drive link conversion, all download failure modes, reduction, upload, size tables, the entire OAuth flow (with a fake Google), the discovery that claude.ai performs, and both MCP transports end to end.

Tests that spend credits or depend on a live network stay out of the suite, in scripts/:

npm run probe            # ~US$ 0,005 — separa "chave ruim" de "pipeline ruim"
npm run smoke:drive      # ~US$ 0,01  — link do Drive de ponta a ponta
npm run smoke:edit       # ~US$ 0,02  — o modelo edita ou só regenera?
npm run smoke:stateless  # ~US$ 0,01  — não deixa um byte para trás

Environment variables

Variable

Default

Purpose

OPENAI_API_KEY

Required with the openai provider

IMAGE_PROVIDER

openai

Switches the image engine

MCP_TRANSPORT

stdio

stdio or http

PORT

8787

HTTP mode port

MCP_PATH

/mcp

MCP endpoint path

MCP_TOKEN

Fixed bearer (script and test; claude.ai doesn't accept)

MCP_BASE_URL

Required with OAuth: it's the issuer, and must be fixed

GOOGLE_CLIENT_ID / GOOGLE_CLIENT_SECRET

Enable OAuth

MCP_EMAILS

Who can authorize. A valid Google account is not permission

PERSIST_OUTPUT

false

Saves the full resolution in out/ (local dev only)

ARTIFACT_TTL_MS

900000

Download link validity

ARTIFACT_MAX_BYTES

134217728

Memory ceiling for the piece repository


Hosting (EasyPanel, or any container host)

The server keeps state in memory on purpose — OAuth clients, tokens, and the piece repository are Map(). This requires one live, single process, and that's what rules out serverless platforms: there, POST /register would land on one instance and GET /authorize on another, which doesn't know the client. Login would fail intermittently, with a symptom that doesn't look like the cause.

That's why the deploy is a container, and the rule applies to any host: one replica only. To scale beyond that, replace the three in-memory stores with Redis first.

The Dockerfile at the root serves any container platform. The steps below are for EasyPanel; on another host the interface changes, not the content.

The domain comes first

Google does not accept an IP address as an OAuth redirect, and requires HTTPS. In other words, a domain is a prerequisite, not a finishing touch.

Point an A record of the subdomain to the server IP. Those without a domain can use wildcard DNS — mcp.<ip-with-hyphens>.sslip.io resolves on its own to the IP embedded in the name, and Let's Encrypt issues normally as long as port 80 is open.

Service

  1. Create service → App, with source in this repository and branch main.

  2. Build: Dockerfile, at the root.

  3. Environment:

    Variable

    Value

    PORT

    8787

    MCP_BASE_URL

    https://<your-domain> — no trailing slash

    OPENAI_API_KEY

    the OpenAI key

    GOOGLE_CLIENT_ID

    from the OAuth client (Web Application)

    GOOGLE_CLIENT_SECRET

    from the same client

    MCP_EMAILS

    who can authorize, comma-separated

    MCP_TRANSPORT=http already comes from the Dockerfile — don't set it.

  4. Domains: the subdomain pointing to port 8787, with HTTPS enabled.

  5. Deploy.

  6. Google Cloud Console → Credentials → your OAuth client, add the authorized redirect, exactly:

    https://<seu-dominio>/oauth/google/callback
  7. claude.ai → connectors: https://<your-domain>/mcp.

The MCP_BASE_URL becomes the OAuth issuer and is compared character by character with what the client discovers. A domain different from the configured one, or a trailing slash, makes the link fail without a useful message.

Checking

curl https://<seu-dominio>/health

The field that matters is "auth":"oauth". If it comes back as "none", some Google variable didn't arrive — and then the server came up open, accepting any call and spending the host's key.


API notes that save debugging time

  • The K in image_size is uppercase. 2k is rejected.

  • gpt-image-2 accepts any WxH divisible by 16; the smaller ones only accept three fixed sizes. Final story/reels needs gpt-image-2.

  • In editing, the image comes before the text in the input array.

  • There's no chained regeneration in the openai provider: previous_interaction_id is from the Gemini Interactions API. To adjust, resend the image as a reference.

  • URL input sends its own User-Agent: several origins (Wikimedia among them) return 400/403 for requests without an identifiable UA.

  • Piece with text: define the copy first, then request the image with that copy.

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 on-brand images from your AI agent: design, edit, and render templates over MCP.

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

  • Generate and manage AI UGC video ads through eleven typed MCP tools

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/WrMaxMarketing/wrmmax-criativo-mcp'

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