codex-imagegen-mcp 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (105) hide show
  1. package/CHANGELOG.md +96 -0
  2. package/LICENSE +201 -0
  3. package/NOTICE +19 -0
  4. package/README.md +373 -0
  5. package/dist/src/auth/borrowed.js +77 -0
  6. package/dist/src/auth/borrowed.js.map +1 -0
  7. package/dist/src/auth/browser-login.js +179 -0
  8. package/dist/src/auth/browser-login.js.map +1 -0
  9. package/dist/src/auth/device-login.js +39 -0
  10. package/dist/src/auth/device-login.js.map +1 -0
  11. package/dist/src/auth/jwt.js +69 -0
  12. package/dist/src/auth/jwt.js.map +1 -0
  13. package/dist/src/auth/manager.js +289 -0
  14. package/dist/src/auth/manager.js.map +1 -0
  15. package/dist/src/auth/oauth.js +246 -0
  16. package/dist/src/auth/oauth.js.map +1 -0
  17. package/dist/src/auth/pages.js +39 -0
  18. package/dist/src/auth/pages.js.map +1 -0
  19. package/dist/src/auth/pkce.js +14 -0
  20. package/dist/src/auth/pkce.js.map +1 -0
  21. package/dist/src/auth/store.js +75 -0
  22. package/dist/src/auth/store.js.map +1 -0
  23. package/dist/src/backend/images-client.js +223 -0
  24. package/dist/src/backend/images-client.js.map +1 -0
  25. package/dist/src/backend/ratelimits.js +183 -0
  26. package/dist/src/backend/ratelimits.js.map +1 -0
  27. package/dist/src/cli.js +407 -0
  28. package/dist/src/cli.js.map +1 -0
  29. package/dist/src/config.js +87 -0
  30. package/dist/src/config.js.map +1 -0
  31. package/dist/src/constants.js +52 -0
  32. package/dist/src/constants.js.map +1 -0
  33. package/dist/src/doctor.js +122 -0
  34. package/dist/src/doctor.js.map +1 -0
  35. package/dist/src/errors.js +76 -0
  36. package/dist/src/errors.js.map +1 -0
  37. package/dist/src/generation.js +183 -0
  38. package/dist/src/generation.js.map +1 -0
  39. package/dist/src/history.js +47 -0
  40. package/dist/src/history.js.map +1 -0
  41. package/dist/src/images/chroma.js +200 -0
  42. package/dist/src/images/chroma.js.map +1 -0
  43. package/dist/src/images/codec.js +159 -0
  44. package/dist/src/images/codec.js.map +1 -0
  45. package/dist/src/images/inputs.js +83 -0
  46. package/dist/src/images/inputs.js.map +1 -0
  47. package/dist/src/images/output.js +100 -0
  48. package/dist/src/images/output.js.map +1 -0
  49. package/dist/src/images/preview.js +20 -0
  50. package/dist/src/images/preview.js.map +1 -0
  51. package/dist/src/images/resize.js +135 -0
  52. package/dist/src/images/resize.js.map +1 -0
  53. package/dist/src/install/opencode.js +138 -0
  54. package/dist/src/install/opencode.js.map +1 -0
  55. package/dist/src/install/skill.js +128 -0
  56. package/dist/src/install/skill.js.map +1 -0
  57. package/dist/src/install/snippets.js +127 -0
  58. package/dist/src/install/snippets.js.map +1 -0
  59. package/dist/src/log.js +58 -0
  60. package/dist/src/log.js.map +1 -0
  61. package/dist/src/remove-background.js +101 -0
  62. package/dist/src/remove-background.js.map +1 -0
  63. package/dist/src/server/context.js +2 -0
  64. package/dist/src/server/context.js.map +1 -0
  65. package/dist/src/server/index.js +94 -0
  66. package/dist/src/server/index.js.map +1 -0
  67. package/dist/src/server/instructions.js +20 -0
  68. package/dist/src/server/instructions.js.map +1 -0
  69. package/dist/src/server/login-coordinator.js +73 -0
  70. package/dist/src/server/login-coordinator.js.map +1 -0
  71. package/dist/src/server/progress.js +33 -0
  72. package/dist/src/server/progress.js.map +1 -0
  73. package/dist/src/server/prompts.js +45 -0
  74. package/dist/src/server/prompts.js.map +1 -0
  75. package/dist/src/server/resources.js +73 -0
  76. package/dist/src/server/resources.js.map +1 -0
  77. package/dist/src/server/tools.js +338 -0
  78. package/dist/src/server/tools.js.map +1 -0
  79. package/dist/src/server/workspace.js +45 -0
  80. package/dist/src/server/workspace.js.map +1 -0
  81. package/dist/src/status.js +86 -0
  82. package/dist/src/status.js.map +1 -0
  83. package/dist/src/util/format.js +36 -0
  84. package/dist/src/util/format.js.map +1 -0
  85. package/dist/src/util/fs.js +117 -0
  86. package/dist/src/util/fs.js.map +1 -0
  87. package/dist/src/util/http.js +65 -0
  88. package/dist/src/util/http.js.map +1 -0
  89. package/dist/src/util/invocation.js +43 -0
  90. package/dist/src/util/invocation.js.map +1 -0
  91. package/dist/src/util/open.js +41 -0
  92. package/dist/src/util/open.js.map +1 -0
  93. package/docs/ARCHITECTURE.md +123 -0
  94. package/docs/AUTH.md +242 -0
  95. package/docs/BACKEND.md +169 -0
  96. package/docs/CLIENTS.md +154 -0
  97. package/docs/DEVELOPMENT.md +171 -0
  98. package/docs/README.md +84 -0
  99. package/docs/TOOLS.md +221 -0
  100. package/package.json +67 -0
  101. package/skill/imagegen/LICENSE.txt +201 -0
  102. package/skill/imagegen/SKILL.md +189 -0
  103. package/skill/imagegen/references/prompting.md +112 -0
  104. package/skill/imagegen/references/sample-prompts.md +418 -0
  105. package/skill/imagegen/references/tools.md +90 -0
@@ -0,0 +1,171 @@
1
+ <p align="center">
2
+ <img src="assets/banner-development.jpg" width="100%" alt="Development — a travel-poster illustration of a surveyor's camp: a brass theodolite on a tripod aimed at a distant peak, a canvas tent and a work table with a toolbox">
3
+ </p>
4
+
5
+ # Development
6
+
7
+ Build it, test it without touching OpenAI, test it live, and ship it.
8
+
9
+ **On this page:** [Setup](#setup) · [Layout](#layout) · [Tests](#tests) · [Continuous integration](#continuous-integration) · [Live testing](#live-testing) · [Conventions](#conventions) · [Artwork](#artwork) · [Updating from upstream Codex](#updating-from-upstream-codex) · [Releasing](#releasing)
10
+
11
+ ## Setup
12
+
13
+ You need Node.js 22 or newer.
14
+
15
+ ```bash
16
+ git clone https://github.com/ShalomObongo/codex-imagegen-mcp.git && cd codex-imagegen-mcp
17
+ npm ci
18
+ npm run build # tsc → dist/, marks dist/src/cli.js executable (works on Windows too)
19
+ npm test # build + the node:test suite
20
+ npm run typecheck
21
+ npm link # optional: expose `codex-imagegen-mcp` globally for manual testing
22
+ ```
23
+
24
+ Contribution guidelines, commit style and the pull-request checklist are in [CONTRIBUTING.md](../.github/CONTRIBUTING.md).
25
+
26
+ ## Layout
27
+
28
+ ```text
29
+ src/ TypeScript sources (see ARCHITECTURE.md)
30
+ test/ node:test suites; test/helpers/mock-openai.ts mocks auth.openai.com + the ChatGPT backend
31
+ skill/imagegen/ the Agent Skill shipped with the server
32
+ upstream/ byte-exact copy of the Codex skill it was adapted from
33
+ scripts/ compose-doc-art.py builds docs/assets from raw generations
34
+ docs/ this documentation; docs/assets holds the artwork
35
+ ```
36
+
37
+ ## Tests
38
+
39
+ The suite never talks to OpenAI. Every test runs against a scriptable mock and an isolated temp home:
40
+
41
+ ```mermaid
42
+ flowchart LR
43
+ tests["node:test suites"]:::ink
44
+ subgraph sandbox ["isolated temp home (test/helpers/env.ts)"]
45
+ server["codex-imagegen-mcp<br/>(in-process or over stdio)"]:::rust
46
+ files[("auth.json · history · images")]:::cream
47
+ end
48
+ subgraph mock ["test/helpers/mock-openai.ts"]
49
+ oauth["OAuth: PKCE-checked code exchange<br/>rotating single-use refresh tokens · revoke · device code"]:::teal
50
+ api["backend: images/generations · images/edits · wham/usage<br/>scripted 401 · 429 · 5xx · policy · Cloudflare"]:::teal
51
+ end
52
+ tests --> server
53
+ server --> files
54
+ server -- "CODEX_IMAGEGEN_AUTH_ISSUER" --> oauth
55
+ server -- "CODEX_IMAGEGEN_BASE_URL" --> api
56
+ classDef ink fill:#2A2523,stroke:#9A8C76,color:#E4D9C6
57
+ classDef rust fill:#A6553B,stroke:#7E3F2B,color:#FFFFFF
58
+ classDef teal fill:#4E6E63,stroke:#3A544B,color:#FFFFFF
59
+ classDef cream fill:#E4D9C6,stroke:#A89A80,color:#2A2523
60
+ style sandbox fill:#D9A05B1F,stroke:#B5813F,color:#B5813F
61
+ style mock fill:#D9A05B1F,stroke:#B5813F,color:#B5813F
62
+ ```
63
+
64
+ - **The mock behaves like the real service where it matters.** It checks the PKCE `code_verifier` against the challenge and the `redirect_uri`, and its refresh tokens are **single-use**: a second use returns `refresh_token_reused`, just as OpenAI does.
65
+ - **Scripting failures:** queue responses in `state.imageQueue` to simulate 401, 429, 5xx, policy and Cloudflare errors, and set delays to exercise timeouts and locking.
66
+ - **Nothing real is touched.** `test/helpers/env.ts` builds an isolated `CODEX_IMAGEGEN_*` environment in a temp directory, so tests never read or write your credentials. The installer tests also point `HOME` and `XDG_CONFIG_HOME` at temp directories.
67
+
68
+ | Suite | Covers |
69
+ |---|---|
70
+ | `auth-primitives` | JWT claims, PKCE, authorize URL, the 0600 store, the cross-process lock, stale locks |
71
+ | `auth-manager` | Refresh (three concurrent processes → one refresh), permanent vs transient failures, borrowed sources, 401 recovery, revoke |
72
+ | `login-flows` | Browser round trip, state mismatch, authorize errors, failed exchange, cancel, timeout, device code |
73
+ | `images-client` | Exact request body and headers, edits, retries, usage limits, policy/invalid/Cloudflare mapping, the usage API |
74
+ | `image-processing` | Codec, previews, chroma key, output planning, no-overwrite writes, input validation |
75
+ | `install` | JSONC-preserving opencode edits, backups, idempotency, conflict protection, uninstall, snippets |
76
+ | `server` | Full MCP over stdio: tools, resources, prompts, progress, errors, sign-in via the tool and then generation |
77
+ | `cli` | Every command end to end against the mock |
78
+
79
+ ## Continuous integration
80
+
81
+ Every push to `main` and every pull request runs [CI](../.github/workflows/ci.yml):
82
+
83
+ | Job | What it checks |
84
+ |---|---|
85
+ | **Node 22 · 24 · 26** on Linux | `npm ci`, `npm run typecheck`, `npm test`, a CLI smoke test |
86
+ | **Node 24** on macOS and Windows | The same, on the other two platforms |
87
+ | **Package and install** | `npm pack`, a global install of the tarball, and that the installed CLI runs and ships its skill |
88
+
89
+ Actions are pinned to commit SHAs, and [Dependabot](../.github/dependabot.yml) keeps them and the npm dependencies current. CodeQL code scanning runs on GitHub's default setup.
90
+
91
+ ## Live testing
92
+
93
+ > [!CAUTION]
94
+ > These commands use your real ChatGPT quota. `status`, `doctor` and `auth_status` are free; every generated image counts.
95
+
96
+ ```bash
97
+ node dist/src/cli.js status # auth + usage, no quota
98
+ node dist/src/cli.js generate "a red apple" -o tmp/live/apple.png
99
+ node dist/src/cli.js install opencode && opencode mcp list
100
+ opencode run -m openai/gpt-5.5 "make a transparent sticker of a cactus, save to assets/cactus.png"
101
+ opencode run -m github-copilot/claude-sonnet-5 "…" # Copilot providers take a different media path in opencode
102
+ ```
103
+
104
+ `tmp/` is git-ignored. To see what the server did inside a client, set `CODEX_IMAGEGEN_LOG_LEVEL=debug` and read `~/.local/share/codex-imagegen-mcp/server.log`.
105
+
106
+ ## Conventions
107
+
108
+ - **stdout is the protocol.** Never write to it from server code paths.
109
+ - **Errors carry the next step.** Raise `ImagegenError(kind, message)`; each kind maps to that step in `describeError`.
110
+ - **Limits live in descriptions too.** Some clients strip schema constraints before the model sees them.
111
+ - **Claims about the backend must be measured.** Record them in [Backend](BACKEND.md) with the date.
112
+ - **The skill tracks upstream.** Diff changes against `upstream/codex-imagegen-skill/` and keep the prompting guidance aligned.
113
+
114
+ ## Artwork
115
+
116
+ Every image in the docs was generated with this server, following the art-direction record in [docs/assets](assets/README.md). The raw generations stay out of git (about 2 MB each). `scripts/compose-doc-art.py` turns them into the committed assets:
117
+
118
+ ```bash
119
+ # 1. regenerate any source with the prompts in docs/assets/README.md, saving into tmp/art/
120
+ # 2. rebuild every committed asset (Pillow + numpy; Superclarendon ships with macOS)
121
+ python3 scripts/compose-doc-art.py --art tmp/art --out docs/assets
122
+ ```
123
+
124
+ The script does five things:
125
+ - **Snaps alpha.** The service returns 251–254 inside opaque areas; this rounds it to 255.
126
+ - **Typesets wording locally.** Poster titles are set in Superclarendon, so no text is ever generated.
127
+ - **Slices the badge grid** into six PNGs.
128
+ - **Builds the showcase sheets.**
129
+ - **Writes the social preview.**
130
+
131
+ Paper grain is seeded, so reruns are byte-stable.
132
+
133
+ ## Updating from upstream Codex
134
+
135
+ 1. **Extract the current skill:** `CODEX_HOME=$(mktemp -d) /Applications/ChatGPT.app/Contents/Resources/codex debug prompt-input hi >/dev/null`. This installs the embedded system skills into `$CODEX_HOME/skills/.system/`.
136
+ 2. **Diff and port.** Compare that `imagegen/` with `upstream/codex-imagegen-skill/`, update the copy, and port the relevant guidance into `skill/imagegen/`.
137
+ 3. **Check the tool.** Look at `codex-rs/ext/image-generation/src/tool.rs` upstream for changes to the request body, model id or limits.
138
+
139
+ ## Releasing
140
+
141
+ Releases are automated. A maintainer only has to:
142
+
143
+ 1. Make sure `main` is green in CI, and run the live smoke test: `status`, one `generate`, one edit with `-b transparent`, `doctor`, plus one `opencode run`.
144
+ 2. Bump `version` in `package.json` (then `npm install --package-lock-only`) and add a `## [X.Y.Z] - YYYY-MM-DD` entry to `CHANGELOG.md`, with its compare link at the bottom.
145
+ 3. Commit, then tag and push: `git tag -a vX.Y.Z -m "…" && git push origin main vX.Y.Z`.
146
+
147
+ The [release workflow](../.github/workflows/release.yml) then:
148
+
149
+ ```mermaid
150
+ flowchart LR
151
+ tag(["push tag vX.Y.Z"]):::ink --> test["npm ci · npm test"]:::teal
152
+ test --> check{"tag = package.json<br/>version?"}:::cream
153
+ check -- "no" --> fail(["fail the release"]):::ochre
154
+ check -- "yes" --> pack["npm pack<br/>+ stable-named copy<br/>+ SHA256SUMS"]:::rust
155
+ pack --> notes["notes from<br/>CHANGELOG.md"]:::rust
156
+ notes --> attest["sign build-provenance<br/>attestation"]:::teal
157
+ attest --> release(["GitHub release<br/>+ announcement discussion"]):::ink
158
+ classDef ink fill:#2A2523,stroke:#9A8C76,color:#E4D9C6
159
+ classDef rust fill:#A6553B,stroke:#7E3F2B,color:#FFFFFF
160
+ classDef ochre fill:#D9A05B,stroke:#B5813F,color:#2A2523
161
+ classDef teal fill:#4E6E63,stroke:#3A544B,color:#FFFFFF
162
+ classDef cream fill:#E4D9C6,stroke:#A89A80,color:#2A2523
163
+ ```
164
+
165
+ Each release carries `codex-imagegen-mcp-X.Y.Z.tgz`, the same file as `codex-imagegen-mcp.tgz` (so `releases/latest/download/codex-imagegen-mcp.tgz` always points at the newest build), and `SHA256SUMS`. Anyone can verify a download with `gh attestation verify codex-imagegen-mcp-X.Y.Z.tgz --repo ShalomObongo/codex-imagegen-mcp`.
166
+
167
+ The package itself contains `dist/src`, `skill`, `docs/*.md`, `README.md`, `CHANGELOG.md`, `LICENSE` and `NOTICE`; the artwork is excluded to keep it small. `npm pack --dry-run` shows the list.
168
+
169
+ ---
170
+
171
+ <p align="center"><a href="ARCHITECTURE.md">← Architecture</a> &nbsp;·&nbsp; <a href="README.md">Docs home</a> &nbsp;·&nbsp; <a href="../README.md">README</a></p>
package/docs/README.md ADDED
@@ -0,0 +1,84 @@
1
+ <p align="center">
2
+ <img src="assets/banner-docs.jpg" width="100%" alt="Documentation — a travel-poster illustration of a wooden signpost with blank arrow boards at a desert crossroads, roads fanning out toward a sun rising behind a peak">
3
+ </p>
4
+
5
+ # Documentation
6
+
7
+ codex-imagegen-mcp puts the image generation of OpenAI Codex into any MCP client, running on your ChatGPT plan. These pages cover how to use it, how it signs in, where it plugs in, and how it works.
8
+
9
+ <table>
10
+ <tr>
11
+ <td width="33%" valign="top">
12
+ <a href="TOOLS.md"><img src="assets/thumbs/banner-tools.jpg" alt="Tools"></a><br>
13
+ <b>1 · <a href="TOOLS.md">Tools</a></b><br>
14
+ <sub>Parameters, results, resources, prompts, errors, progress and timeouts.</sub>
15
+ </td>
16
+ <td width="33%" valign="top">
17
+ <a href="AUTH.md"><img src="assets/thumbs/banner-auth.jpg" alt="Authentication"></a><br>
18
+ <b>2 · <a href="AUTH.md">Authentication</a></b><br>
19
+ <sub>Browser and device-code sign-in, token storage and rotation, borrowing, security.</sub>
20
+ </td>
21
+ <td width="33%" valign="top">
22
+ <a href="CLIENTS.md"><img src="assets/thumbs/banner-clients.jpg" alt="Clients"></a><br>
23
+ <b>3 · <a href="CLIENTS.md">Clients</a></b><br>
24
+ <sub>opencode (automated), Claude Code, Claude Desktop, Cursor, VS Code, Windsurf, Gemini CLI, Codex.</sub>
25
+ </td>
26
+ </tr>
27
+ <tr>
28
+ <td width="33%" valign="top">
29
+ <a href="BACKEND.md"><img src="assets/thumbs/banner-backend.jpg" alt="Backend"></a><br>
30
+ <b>4 · <a href="BACKEND.md">Backend</a></b><br>
31
+ <sub>The Codex skill and built-in tool, the HTTP API, and what the service really honors.</sub>
32
+ </td>
33
+ <td width="33%" valign="top">
34
+ <a href="ARCHITECTURE.md"><img src="assets/thumbs/banner-architecture.jpg" alt="Architecture"></a><br>
35
+ <b>5 · <a href="ARCHITECTURE.md">Architecture</a></b><br>
36
+ <sub>Module map, request flow, and the reasoning behind each design decision.</sub>
37
+ </td>
38
+ <td width="33%" valign="top">
39
+ <a href="DEVELOPMENT.md"><img src="assets/thumbs/banner-development.jpg" alt="Development"></a><br>
40
+ <b>6 · <a href="DEVELOPMENT.md">Development</a></b><br>
41
+ <sub>Build, the mock-backed test suite, live testing, artwork, releases.</sub>
42
+ </td>
43
+ </tr>
44
+ </table>
45
+
46
+ ## Where to start
47
+
48
+ | If you want to… | Read |
49
+ |---|---|
50
+ | Install it and make a first image | The [README quick start](../README.md#quick-start), then [Tools](TOOLS.md) |
51
+ | Know how it signs in, and whether that's safe | [Authentication](AUTH.md) |
52
+ | Use it outside opencode | [Clients](CLIENTS.md) |
53
+ | Understand what Codex does under the hood | [Backend](BACKEND.md) |
54
+ | Change the code | [Architecture](ARCHITECTURE.md), then [Development](DEVELOPMENT.md) |
55
+
56
+ ## At a glance
57
+
58
+ ```mermaid
59
+ flowchart LR
60
+ agent["Your coding agent<br/>opencode · Claude Code · Cursor · VS Code"]:::ink
61
+ server["codex-imagegen-mcp<br/>local stdio MCP server"]:::rust
62
+ skill["imagegen skill<br/>how to prompt, where to save"]:::ochre
63
+ oauth["auth.openai.com<br/>ChatGPT sign-in"]:::teal
64
+ backend["chatgpt.com/backend-api/codex<br/>/images/generations · /images/edits"]:::teal
65
+ files[("your workspace<br/>assets/hero.png")]:::cream
66
+ skill -. guides .-> agent
67
+ agent -- "tools/call" --> server
68
+ server -- "OAuth · refresh" --> oauth
69
+ server -- "Bearer token" --> backend
70
+ server -- "PNG, never overwritten" --> files
71
+ classDef ink fill:#2A2523,stroke:#9A8C76,color:#E4D9C6
72
+ classDef rust fill:#A6553B,stroke:#7E3F2B,color:#FFFFFF
73
+ classDef ochre fill:#D9A05B,stroke:#B5813F,color:#2A2523
74
+ classDef teal fill:#4E6E63,stroke:#3A544B,color:#FFFFFF
75
+ classDef cream fill:#E4D9C6,stroke:#A89A80,color:#2A2523
76
+ ```
77
+
78
+ ## About the artwork
79
+
80
+ Every picture in these docs was generated **with codex-imagegen-mcp itself**, using the `imagegen` skill it ships: the poster banners, the badges, the logo and the examples. The series follows one art direction, a 1930s WPA silkscreen travel poster in five flat inks. [The prompts, the direction record and how to rebuild the assets →](assets/README.md)
81
+
82
+ ---
83
+
84
+ <p align="center"><a href="../README.md">← Back to the README</a> &nbsp;·&nbsp; <a href="TOOLS.md">Start with Tools →</a></p>
package/docs/TOOLS.md ADDED
@@ -0,0 +1,221 @@
1
+ <p align="center">
2
+ <img src="assets/banner-tools.jpg" width="100%" alt="Tools — a travel-poster illustration of five colossal tools planted upright on a desert plain: a paintbrush, a pencil, scissors, a compass and a key">
3
+ </p>
4
+
5
+ # Tools
6
+
7
+ The server exposes **five tools**, three **resources** and two **prompts**. This page documents every parameter, every result field and every error.
8
+
9
+ **On this page:** [Choosing a tool](#choosing-a-tool) · [generate_image](#generate_image) · [edit_image](#edit_image) · [remove_background](#remove_background) · [auth_status](#auth_status) · [sign_in](#sign_in) · [Errors](#errors) · [Resources](#resources) · [Prompts](#prompts) · [Progress, timeouts and cancellation](#progress-timeouts-and-cancellation)
10
+
11
+ | Tool | Purpose | Network | Quota |
12
+ |---|---|---|---|
13
+ | [`generate_image`](#generate_image) | A new image from a text prompt | ChatGPT | 1 image per variant |
14
+ | [`edit_image`](#edit_image) | Edit images, or generate guided by 1–5 references | ChatGPT | 1 image per variant |
15
+ | [`remove_background`](#remove_background) | Key out a flat, solid-colour backdrop | none | none |
16
+ | [`auth_status`](#auth_status) | Sign-in, plan, credential source, usage windows | ChatGPT (usage only) | none |
17
+ | [`sign_in`](#sign_in) | Start a ChatGPT sign-in and return a link or code | OpenAI auth | none |
18
+
19
+ The server's name in client configs defaults to `imagegen`, and clients add a prefix to the tool names:
20
+
21
+ | Client | Tool name as the model sees it |
22
+ |---|---|
23
+ | opencode | `imagegen_generate_image` |
24
+ | Claude Code, OpenAI Codex | `mcp__imagegen__generate_image` |
25
+ | Other clients | listed under the `imagegen` server |
26
+
27
+ > [!NOTE]
28
+ > **Relative paths** resolve against the **workspace root**. That is the first `file://` root the client reports through MCP roots (opencode reports the project directory). Without one, the server uses the directory it was started in. A filesystem root such as `/` falls back to your home directory.
29
+
30
+ ## Choosing a tool
31
+
32
+ ```mermaid
33
+ flowchart LR
34
+ start(["request"]):::ink --> q1{"existing<br/>image?"}:::cream
35
+ q1 -- "no" --> gen["generate_image"]:::rust
36
+ q1 -- "yes" --> q2{"only a flat<br/>backdrop to remove?"}:::cream
37
+ q2 -- "yes" --> rb["remove_background<br/>local · no quota"]:::teal
38
+ q2 -- "no" --> edit["edit_image<br/>edit or use as reference"]:::rust
39
+ gen --> q3{"needs<br/>transparency?"}:::cream
40
+ edit --> q3
41
+ q3 -- "yes" --> tr["background: transparent<br/>check 'alpha verified'"]:::ochre
42
+ q3 -- "no" --> done(["save to output_path,<br/>report the path"]):::ink
43
+ tr --> done
44
+ classDef ink fill:#2A2523,stroke:#9A8C76,color:#E4D9C6
45
+ classDef rust fill:#A6553B,stroke:#7E3F2B,color:#FFFFFF
46
+ classDef ochre fill:#D9A05B,stroke:#B5813F,color:#2A2523
47
+ classDef teal fill:#4E6E63,stroke:#3A544B,color:#FFFFFF
48
+ classDef cream fill:#E4D9C6,stroke:#A89A80,color:#2A2523
49
+ ```
50
+
51
+ ## generate_image
52
+
53
+ Generate a new image from a text prompt. The server calls `POST https://chatgpt.com/backend-api/codex/images/generations`.
54
+
55
+ | Parameter | Type | Default | Description |
56
+ |---|---|---|---|
57
+ | `prompt` | string, 1–32 000 chars | required | The image description. Structure it: use case, subject, style, composition, lighting, palette, exact text in quotes, constraints. |
58
+ | `aspect_ratio` | enum | `auto` | `1:1` `4:5` `5:4` `4:3` `3:4` `3:2` `2:3` `16:9` `9:16` `21:9` `9:21`. Appended to the prompt as, for example, `Aspect ratio: 16:9, wide landscape (horizontal) canvas.` |
59
+ | `background` | enum | `auto` | `transparent` returns a PNG with real alpha. `opaque` asks for a filled background. |
60
+ | `n` | int, 1–4 | `1` | Variants of the *same* prompt, sent as concurrent requests. Each counts against the quota. |
61
+ | `output_path` | string | image library | A `.png`, `.jpg` or `.jpeg` file, or a directory (existing, or ending in `/`). With `n > 1`, `-1`, `-2`… is inserted before the extension. |
62
+ | `output_format` | `png` · `jpeg` | `png` | JPEG is converted locally, flattened on white, and can't be transparent. It's inferred from the `output_path` extension. |
63
+ | `overwrite` | bool | `false` | When false, an existing file is never replaced; the next free `name-2.png`, `name-3.png`… is used. |
64
+ | `include_preview` | bool | `true` | Attach a JPEG preview (longest edge 1024 px, transparency shown as a checkerboard). |
65
+
66
+ > [!TIP]
67
+ > The service picks the pixel size itself and sizes the canvas from the prompt. Measured results: `16:9` → 1672×941, `9:16` → 941×1672, `21:9` → 1916×821, `1:1` → 1254×1254. For exact dimensions, generate the closest ratio, then resize or crop locally.
68
+
69
+ > [!NOTE]
70
+ > `background: "opaque"` is a **hint**. In one measured case the service returned a transparent PNG when the prompt described a backdrop that "will be keyed out", even though `opaque` was sent. If you need a filled background, describe it as part of the picture.
71
+
72
+ **Default location** (no `output_path`): `$CODEX_IMAGEGEN_OUTPUT_DIR`, which defaults to `~/.local/share/codex-imagegen-mcp/images`, in `/YYYY-MM-DD/HHMMSS-<prompt-slug>.png`.
73
+
74
+ **The result has three parts:**
75
+
76
+ 1. **Text.** One line per saved file (path, dimensions, format, size, background, id), then any warnings, then guidance for the model.
77
+ 2. **Image blocks.** One preview per saved file, when `include_preview` is on.
78
+ 3. **`structuredContent`**, validated against the declared `outputSchema`:
79
+
80
+ ```json
81
+ {
82
+ "images": [
83
+ { "id": "img_aa289474550c", "path": "/abs/project/assets/hero.png", "mime_type": "image/png",
84
+ "bytes": 2063882, "width": 1672, "height": 941, "background": "opaque" }
85
+ ],
86
+ "prompt": "…the exact prompt sent, including the aspect-ratio line…",
87
+ "elapsed_ms": 22834,
88
+ "failures": [],
89
+ "warnings": []
90
+ }
91
+ ```
92
+
93
+ When transparency was requested, each image also carries `transparent: true|false`. It records whether the decoded PNG actually contains non-opaque pixels, which is what the text reports as *alpha verified*.
94
+
95
+ **Partial success:** with `n > 1`, finished variants are saved and returned, and failed ones are listed in `failures`. The call is an error (`isError: true`) only if nothing was produced.
96
+
97
+ ## edit_image
98
+
99
+ Edit images, or generate using reference images. The server calls `POST https://chatgpt.com/backend-api/codex/images/edits` with the images inlined as data URLs. It takes every `generate_image` parameter, plus:
100
+
101
+ | Parameter | Type | Description |
102
+ |---|---|---|
103
+ | `images` | string[], 1–5 | Local paths (absolute, `~/…`, workspace-relative), `file://` URLs, `http(s)://` URLs (downloaded with a 60 s timeout), or `data:image/…;base64,…`. PNG, JPEG or WebP, up to 15 MB each, detected by magic bytes. |
104
+
105
+ - `images[0]` is **Image 1**, the primary edit target. Refer to the inputs by index in the prompt, and spell out what must stay unchanged.
106
+ - The input files are never modified.
107
+ - To iterate on a result, pass the previous output path as Image 1.
108
+
109
+ ```text
110
+ Image 1: add a tiny woven straw hat on top of the cactus, tilted slightly.
111
+ Keep the cactus, flowers, pot, outline and transparent background exactly unchanged.
112
+ ```
113
+
114
+ ## remove_background
115
+
116
+ A local chroma-key cutout, ported to TypeScript from the Codex skill's `scripts/remove_chroma_key.py`. It uses the same algorithm, makes no network call and costs no quota.
117
+
118
+ | Parameter | Type | Default | Description |
119
+ |---|---|---|---|
120
+ | `input_path` | string | required | PNG or JPEG |
121
+ | `output_path` | string (`.png`) | `<input>-transparent.png` | Never overwrites unless `overwrite: true` |
122
+ | `key_color` | `#rrggbb` | auto | When omitted, the median of a 6 px border band |
123
+ | `soft_matte` | bool | `true` | Smoothstep alpha between `transparent_threshold` and `opaque_threshold`, combined with key-channel dominance |
124
+ | `despill` | bool | `true` | Caps key-coloured channels on semi-transparent pixels |
125
+ | `tolerance` | 0–255 | `12` | Hard key (`soft_matte: false`): the maximum per-channel distance treated as background |
126
+ | `transparent_threshold` | 0–255 | `12` | Soft matte: at or below this distance a pixel is fully transparent |
127
+ | `opaque_threshold` | 0–255 | `96` | Soft matte: at or above this distance a pixel is fully opaque |
128
+ | `edge_contract` | 0–16 | `0` | Erode the matte N px (3×3 minimum filter) to remove halos |
129
+ | `edge_feather` | 0–64 | `0` | Gaussian blur radius for the alpha edge |
130
+ | `overwrite`, `include_preview` | bool | `false`, `true` | As above |
131
+
132
+ The result reports the key colour it used and the percentage of fully transparent and soft-edge pixels. It warns when nothing matched the key, or when more than 97% of the image became transparent.
133
+
134
+ <p align="center">
135
+ <img src="assets/examples/remove-background.jpg" width="78%" alt="A striped balloon generated on a flat green backdrop, and the same balloon cut out with remove_background">
136
+ <br><sub>Real run: key auto-detected as <code>#03f902</code> · 63.8% fully transparent · 0.4% soft edge pixels.</sub>
137
+ </p>
138
+
139
+ ## auth_status
140
+
141
+ | Parameter | Default | Description |
142
+ |---|---|---|
143
+ | `check_usage` | `true` | Also fetch `GET /backend-api/wham/usage` (free) for the usage windows |
144
+
145
+ The text report covers:
146
+ - the active source, account email, plan and account suffix;
147
+ - when the token expires;
148
+ - the usage windows, e.g. `Codex 5-hour window: 6% used, resets in 32m`;
149
+ - any sign-in in progress, and the outcome of the last one;
150
+ - every credential source with its state: `✓` ready, `·` missing, `✗` expired or unusable, `-` disabled.
151
+
152
+ ## sign_in
153
+
154
+ | Parameter | Default | Description |
155
+ |---|---|---|
156
+ | `method` | `browser` | `browser`: an authorize link that redirects to `http://localhost:1455` on this machine. `device`: a code to enter at `https://auth.openai.com/codex/device` from any device. |
157
+ | `open_browser` | `true` | Try to open the link on this machine |
158
+ | `force` | `false` | Start a new sign-in even when one already exists, e.g. to switch account or workspace |
159
+
160
+ The tool returns the link or code **immediately**, and the sign-in completes in the background. Only one sign-in runs at a time, so a second call returns the pending one. The model should relay the link or code verbatim, then call `auth_status` to confirm. [How sign-in works →](AUTH.md)
161
+
162
+ ## Errors
163
+
164
+ Errors come back as `isError: true` results. The text states both the problem and the next step:
165
+
166
+ | Kind | Typical message → next step |
167
+ |---|---|
168
+ | `not_signed_in` | *Not signed in to ChatGPT. Sign in by running `… login` … or call the `sign_in` tool* |
169
+ | `session_expired` | *Your sign-in was invalidated … Please sign in again.* |
170
+ | `auth_failed` | HTTP 401 means the credentials were rejected; 403 means the plan or workspace lacks the feature |
171
+ | `usage_limit` | *You've hit your ChatGPT image-generation usage limit (plus plan). It resets in 1h 12m …* → don't retry |
172
+ | `content_policy` | *The request was rejected by OpenAI's safety system: …* → rephrase |
173
+ | `invalid_request` · `invalid_input` · `unsupported` | Bad parameters, paths, formats or sizes → fix the arguments |
174
+ | `server_error` · `network` · `timeout` | Transient. The server already retried 5xx and network errors twice (after 1 s and 3 s), so one more try is reasonable |
175
+ | `blocked` | A Cloudflare challenge → try later or from another network |
176
+
177
+ The ChatGPT request id (`x-codex-imagegen-request-id`) is appended when available.
178
+
179
+ ## Resources
180
+
181
+ | URI | Type | Content |
182
+ |---|---|---|
183
+ | `imagegen://history` | `application/json` | The 50 most recent saved images, newest first: id, time, tool, path, size, prompt, inputs, request ids |
184
+ | `imagegen://images/{id}` | image blob | The saved file, or a 2048 px JPEG preview if it is larger than 8 MB. The listing shows the 25 most recent. |
185
+ | `imagegen://skill/SKILL.md`, `imagegen://skill/references/*.md` | `text/markdown` | The bundled skill, for clients without Agent Skills support |
186
+
187
+ History is also appended to `$CODEX_IMAGEGEN_HOME/history.jsonl`.
188
+
189
+ ## Prompts
190
+
191
+ | Prompt | Arguments | Expands to |
192
+ |---|---|---|
193
+ | `generate` | `description` | A request to use `generate_image` with the skill's workflow checklist |
194
+ | `edit` | `image`, `change` | A request to use `edit_image` with invariant-preserving instructions |
195
+
196
+ In opencode they appear as `/imagegen:generate` and `/imagegen:edit`.
197
+
198
+ ## Progress, timeouts and cancellation
199
+
200
+ ```mermaid
201
+ sequenceDiagram
202
+ participant C as MCP client
203
+ participant S as imagegen server
204
+ participant B as ChatGPT backend
205
+ C->>S: tools/call (with a progress token)
206
+ S->>B: POST /images/generations
207
+ loop every 5 s
208
+ S-->>C: notifications/progress, which resets the client's request timeout
209
+ end
210
+ B-->>S: PNG after 15–60 s
211
+ S-->>C: result
212
+ Note over C,S: notifications/cancelled aborts the HTTP request immediately
213
+ ```
214
+
215
+ - **Progress:** while a request runs, the server sends a progress notification every 5 s, provided the client supplied a progress token. opencode supplies one and resets its request timeout on each notification (`resetTimeoutOnProgress`), so 15–60 s generations never hit its 60 s default.
216
+ - **Timeout:** the backend request itself times out after `CODEX_IMAGEGEN_TIMEOUT_MS`, 300 s by default.
217
+ - **Cancellation:** cancelling the tool call aborts the HTTP request.
218
+
219
+ ---
220
+
221
+ <p align="center"><a href="README.md">← Docs home</a> &nbsp;·&nbsp; <a href="AUTH.md">Authentication →</a></p>
package/package.json ADDED
@@ -0,0 +1,67 @@
1
+ {
2
+ "name": "codex-imagegen-mcp",
3
+ "version": "0.1.2",
4
+ "description": "MCP server + Agent Skill that brings OpenAI Codex's image generation to any MCP client (opencode, Claude Code, Cursor, VS Code and more), running on your ChatGPT plan with no API key.",
5
+ "license": "Apache-2.0",
6
+ "author": "Shalom Obongo (https://github.com/ShalomObongo)",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/ShalomObongo/codex-imagegen-mcp.git"
10
+ },
11
+ "homepage": "https://github.com/ShalomObongo/codex-imagegen-mcp#readme",
12
+ "bugs": {
13
+ "url": "https://github.com/ShalomObongo/codex-imagegen-mcp/issues"
14
+ },
15
+ "type": "module",
16
+ "bin": {
17
+ "codex-imagegen-mcp": "dist/src/cli.js"
18
+ },
19
+ "main": "dist/src/server/index.js",
20
+ "files": [
21
+ "dist/src",
22
+ "skill",
23
+ "docs/*.md",
24
+ "README.md",
25
+ "CHANGELOG.md",
26
+ "LICENSE",
27
+ "NOTICE"
28
+ ],
29
+ "engines": {
30
+ "node": ">=22"
31
+ },
32
+ "scripts": {
33
+ "clean": "node -e \"require('node:fs').rmSync('dist', { recursive: true, force: true })\"",
34
+ "build": "npm run clean && tsc -p tsconfig.json && node -e \"require('node:fs').chmodSync('dist/src/cli.js', 0o755)\"",
35
+ "typecheck": "tsc -p tsconfig.json --noEmit",
36
+ "test": "npm run build && node --test --test-concurrency=1 \"dist/test/**/*.test.js\"",
37
+ "start": "node dist/src/cli.js serve",
38
+ "prepack": "npm run build"
39
+ },
40
+ "keywords": [
41
+ "mcp",
42
+ "mcp-server",
43
+ "model-context-protocol",
44
+ "image-generation",
45
+ "image-editing",
46
+ "gpt-image",
47
+ "codex",
48
+ "openai-codex",
49
+ "chatgpt",
50
+ "opencode",
51
+ "claude-code",
52
+ "cursor",
53
+ "agent-skills"
54
+ ],
55
+ "dependencies": {
56
+ "@modelcontextprotocol/sdk": "^1.30.0",
57
+ "jpeg-js": "^0.4.4",
58
+ "jsonc-parser": "^3.3.1",
59
+ "pngjs": "^7.0.0",
60
+ "zod": "^4.6.5"
61
+ },
62
+ "devDependencies": {
63
+ "@types/node": "^26.6.2",
64
+ "@types/pngjs": "^6.0.5",
65
+ "typescript": "^7.0.2"
66
+ }
67
+ }