Skip to main content
Glama
README.md
# Image Generator MCP

A TypeScript MCP Server that exposes `generate_image` to Codex via stdio. Supports general image generation and hatch-pet specific workflows: canonical base generation, identity reference, multi-image input, layout guides, and single-action row chroma-key source images.

This service only calls image models, saves raw results, and performs basic validation. It does NOT handle matting, frame interpolation, sprite sheet assembly, frame slicing, mirroring, or atlas synthesis for hatch-pet.

## Requirements

- Node.js 22+
- pnpm 11+
- OpenAI API Key
- System `curl` when using third-party OpenAI-compatible endpoints

This project uses stable dependency versions tested during implementation: MCP Server `2.0.0`, OpenAI Node SDK `7.1.0`.

## Installation & Build

```bash
cd /path/to/image-generator-mcp
pnpm install
pnpm run typecheck
pnpm test
pnpm run build
```

Never commit real secrets to `.env`, `.env.example`, Codex config, or source code. `.env.example` only lists variable names.

## Environment Variables

Set these in the environment that launches Codex:

```bash
export OPENAI_API_KEY="your local key"
export IMAGE_MODEL="gpt-image-2"
```

The macOS GUI Codex typically does NOT inherit terminal environment variables. Set the GUI environment before launching Codex and fully restart Codex afterward:

```bash
launchctl setenv OPENAI_API_KEY "your local key"
launchctl setenv IMAGE_MODEL "gpt-image-2"
```

The key persists in the current login session. Clear it when done:

```bash
launchctl unsetenv OPENAI_API_KEY
```

Do NOT use `codex mcp add --env OPENAI_API_KEY=...` as this may save the value in Codex configuration. `IMAGE_MODEL` is not sensitive and may be configured via `--env` as needed.

Optional variables:

| Variable | Purpose |
| --- | --- |
| `IMAGE_MODEL` | Default model, falls back to `gpt-image-2` if unset |
| `IMAGE_INPUT_ROOTS` | Additional absolute directories for reading reference images, comma-separated |
| `IMAGE_REQUEST_TIMEOUT_MS` | API request timeout in ms, default 120000, range 1000-600000 |
| `IMAGE_HTTP_TRANSPORT` | `auto`, `fetch`, or `curl`; default `auto` uses curl for third-party base URLs |
| `IMAGE_CONNECT_TIMEOUT_MS` | `curl` connection timeout in ms, default 30000, range 1000-120000 |
| `IMAGE_PROJECT_ROOT` | Project root directory, typically auto-detected |

The project directory is always an allowed input root. Additional paths must be pre-configured via `IMAGE_INPUT_ROOTS`; individual tool calls cannot inject arbitrary read directories.

## Startup & Registration

Run directly from source:

```bash
cd /path/to/image-generator-mcp
pnpm start
```

Codex CLI `0.146.0-alpha.3.1` supports the following stdio registration format:

```bash
codex mcp add image-generator -- /path/to/node /path/to/image-generator-mcp/node_modules/tsx/dist/cli.mjs /path/to/image-generator-mcp/src/index.ts
```

If Node and pnpm are installed globally, register the build artifact instead:

```bash
cd /path/to/image-generator-mcp
pnpm run build
codex mcp add image-generator-dist -- node /path/to/image-generator-mcp/dist/src/index.js
```

After registration, fully quit and restart Codex before beginning new tasks. Verify registration:

```bash
codex mcp list
codex mcp get image-generator
```

## Parameters

| Parameter | Required | Description |
| --- | --- | --- |
| `prompt` | Yes | Authoritative generation prompt, max 40000 chars |
| `provider` | No | Currently only `openai` is allowed |
| `model` | No | Default from `IMAGE_MODEL`, otherwise `gpt-image-2` |
| `size` | No | `1024x1024`, `1536x1024`, `1024x1536`, `auto` |
| `quality` | No | `low`, `medium`, `high`, `auto` |
| `output_format` | No | `png`, `webp`, `jpeg` |
| `background` | No | `opaque`, `transparent`, `chroma-key` |
| `chroma_key_color` | Conditional | `#RRGGBB`; defaults to `#00FF00` if omitted |
| `n` | No | 1-4; fixed to 1 for pet tasks |
| `output_name` | No | Filename stem only, no paths; existing files get `-v2` suffix |
| `negative_prompt` | No | Merged into `Avoid:` paragraph when model has no native field |
| `provider_options` | No | OpenAI currently only allows `moderation: auto\|low` |
| `input_images` | No | Array of reference images with `path` and explicit `role`, max 8 images |
| `task_type` | No | `general`, `pet-base`, `pet-row` |
| `identity_lock` | No | Identity features that must be preserved across hatch-pet states |
| `state_id` | pet-row required | One of 9 hatch-pet states |
| `expected_frame_count` | pet-row required | 2-16, validates only, does not auto-interpolate |
| `input_fidelity` | No | `low` / `high` when supported by the model |

Input image roles: `identity-reference`, `canonical-base`, `layout-guide`, `style-reference`, `edit-target`, `supporting-reference`.

## Model Capabilities

The capability matrix is an explicit allowlist. Unknown models are never guessed or passthrough.

| Model | Text-to-image | Multi-reference edit | Native transparency | Chroma key | input_fidelity |
| --- | --- | --- | --- | --- | --- |
| `gpt-image-2` | Yes | Yes | No | Yes | Not passed |
| `gpt-image-1.5` | Yes | Yes | Yes | Yes | low/high |
| `gpt-image-1` | Yes | Yes | Yes | Yes | low/high |
| `gpt-image-1-mini` | Yes | Yes | No | Yes | low/high |
| `chatgpt-image-latest` | Yes | Yes | Yes | Yes | low/high |

`hatch-pet` uses chroma-key source images by default, making `gpt-image-2` the preferred choice. Callers should only select transparency-capable models when native transparency is explicitly needed. The service will never silently switch models, drop reference images, or ignore parameters.

## General Usage Example

Ask Codex:

```text
Use generate_image to create a large dog barking loudly, realistic photography style, landscape composition. size=1536x1024, quality=high, output_format=png, output_name=barking-dog.
```

Equivalent parameters:

```json
{
  "prompt": "Create a large dog barking loudly, realistic photography style, landscape composition.",
  "size": "1536x1024",
  "quality": "high",
  "output_format": "png",
  "output_name": "barking-dog"
}
```

## Hatch-pet Canonical Base Example

```json
{
  "prompt": "Create one centered full-body tricolor beagle puppy desktop pet, faithful to the attached identity reference.",
  "task_type": "pet-base",
  "model": "gpt-image-2",
  "size": "1024x1024",
  "quality": "high",
  "output_format": "png",
  "background": "chroma-key",
  "chroma_key_color": "#00FFFF",
  "output_name": "beagle-base",
  "identity_lock": "Keep the exact tricolor markings, broad white blaze, floppy ears, puppy body ratio, black sunglasses, and blue bone-shaped collar tag.",
  "input_images": [
    {
      "path": "/absolute/project/reference-beagle.png",
      "role": "identity-reference"
    }
  ]
}
```

## Hatch-pet Single Action Row Example

Generate only one state at a time. `canonical-base` is required; layout guides are used only for frame positioning and spacing.

```json
{
  "prompt": "Create a six-frame idle animation row for the same pet. Use subtle breathing, blinking, and a tiny head bob.",
  "task_type": "pet-row",
  "state_id": "idle",
  "expected_frame_count": 6,
  "model": "gpt-image-2",
  "size": "1536x1024",
  "quality": "high",
  "output_format": "png",
  "background": "chroma-key",
  "chroma_key_color": "#00FFFF",
  "output_name": "beagle-idle",
  "identity_lock": "Preserve the canonical base face, markings, proportions, glasses, tag, material, and silhouette.",
  "input_images": [
    {
      "path": "/absolute/run/references/canonical-base.png",
      "role": "canonical-base"
    },
    {
      "path": "/absolute/run/references/layout-guides/idle.png",
      "role": "layout-guide"
    }
  ]
}
```

Return text first line is compatible with hatch-pet worker:

```text
selected_source=/absolute/project/output/imagegen/beagle-idle.png
qa_note=File decoded and passed basic format, dimension, and background checks; character identity and action semantics still require visual acceptance.
```

Structured result includes model, parameters, prompt hash, input image hashes, output dimensions, file hash, and validation warnings.

## Security Boundaries

- Reference images must use absolute paths within the project or pre-configured root directories.
- Paths checked via `realpath`, blocks `..` and symlink escapes.
- Validates file header, MIME type, decodeability, file size, and pixel count.
- `provider_options` is a strict allowlist; URLs, headers, tokens, proxies, or local paths cannot be passed.
- Output can only write to `output/imagegen/`, using same-directory temp files and atomic hard links.
- Never overwrites existing images; automatically generates `-v2`, `-v3` suffixes.
- Logs never include secrets, base64, full prompts, or reference image binary data.
- `curl` transport passes auth headers via separate stdin; keys never appear in process arguments.
- MCP cancellation signals are forwarded to the OpenAI SDK.
- Original images are preserved on validation failure with warnings; no automatic retouching or frame forgery.

## Testing

Tests do NOT require an API Key; Provider requests are mocked. Run:

```bash
pnpm run typecheck
pnpm test
pnpm run build
pnpm run smoke:mcp
```

For development, directly verify the latest TypeScript source:

```bash
MCP_SMOKE_SOURCE=1 pnpm exec tsx scripts/mcp-smoke.ts
```

Coverage includes parameter validation, output naming, prompt merging, capability rejection, reference image ordering, path and symlink escape tests, OpenAI generations/edits mapping, corrupted images, chroma-key corner detection, action area estimation, versioned filenames, and the `pet-base -> canonical-base -> idle row` integration chain.

Live API testing is excluded from the default suite and will not accidentally incur charges during CI or local development.

## Troubleshooting

`OPENAI_API_KEY_MISSING`: The key isn't reaching the MCP process. Set the environment variable where Codex launches and fully restart Codex.

`INPUT_PATH_FORBIDDEN`: Reference image is outside the project directory. Copy the image into the project, or add precise read-only root directories via `IMAGE_INPUT_ROOTS` before launching Codex.

`BACKGROUND_UNSUPPORTED`: The selected model doesn't support native transparency. Explicitly switch to a transparency-capable model, or use `chroma-key`; the service will not auto-downgrade.

`CANONICAL_BASE_REQUIRED`: Action row is missing canonical base reference. Complete `pet-base` first and use the result as `canonical-base`.

`validation.status=failed`: The file is still saved. Check warnings and perform visual acceptance; do not treat basic area estimation as final identity or motion review.

Network, 429, or 5xx: Failed results are marked `retryable=true`. Retry the current single visual task after checking network and account limits.

`OPENAI_CONNECTION_ERROR` or `OPENAI_CONNECTION_TIMEOUT`: Check the Base URL first. Third-party OpenAI-compatible gateways automatically use `curl` transport; you can also explicitly set `IMAGE_HTTP_TRANSPORT=curl`. Official `https://api.openai.com/v1` continues using Node `fetch` by default.

## Adding New Providers

1. Implement `ImageProvider`.
2. Define explicit capability matrices for each model.
3. Register the provider and service endpoints in static configuration.
4. Add tests for request mapping, unsupported parameters, empty responses, and sanitized errors.
5. Never allow arbitrary base URLs, headers, workflow paths, or local service addresses per-call.

ComfyUI should use admin-preconfigured named endpoints and workflow templates; arbitrary URLs or workflow files must NOT be exposed as tool parameters.

## Official Resources

- [MCP TypeScript SDK](https://github.com/modelcontextprotocol/typescript-sdk)
- [MCP Build a server](https://modelcontextprotocol.io/docs/develop/build-server)
- [OpenAI Image generation](https://developers.openai.com/api/docs/guides/image-generation)
- [OpenAI Node SDK image resource](https://github.com/openai/openai-node/blob/master/src/resources/images.ts)

---

[中文文档 (Chinese Documentation)](./README.zh-CN.md)