@flownex-ai/mcp-gpt-image-2 0.1.0
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.
- package/.claude-plugin/marketplace.json +11 -0
- package/.claude-plugin/plugin.json +40 -0
- package/.mcp.json +14 -0
- package/LICENSE +21 -0
- package/README.md +177 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +522 -0
- package/dist/index.js.map +1 -0
- package/dist/version.d.ts +1 -0
- package/dist/version.js +2 -0
- package/dist/version.js.map +1 -0
- package/package.json +57 -0
- package/skills/generate-image/SKILL.md +58 -0
- package/skills/powerpoint-images/SKILL.md +78 -0
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "mcp-gpt-image-2-plugins",
|
|
3
|
+
"description": "FlowNex AI plugins for Claude Code and Claude Cowork — image generation, presentation imagery, and more.",
|
|
4
|
+
"owner": { "name": "FlowNex AI", "url": "https://flownexai.com" },
|
|
5
|
+
"plugins": [{
|
|
6
|
+
"name": "gpt-image-2",
|
|
7
|
+
"source": { "source": "npm", "package": "@flownex-ai/mcp-gpt-image-2" },
|
|
8
|
+
"description": "Image generation with OpenAI gpt-image-2 via MCP",
|
|
9
|
+
"version": "0.1.0"
|
|
10
|
+
}]
|
|
11
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json",
|
|
3
|
+
"name": "gpt-image-2",
|
|
4
|
+
"description": "Image generation with OpenAI gpt-image-2 — works in Claude Code and Claude Cowork",
|
|
5
|
+
"version": "0.1.0",
|
|
6
|
+
"author": { "name": "FlowNex AI", "url": "https://flownexai.com" },
|
|
7
|
+
"homepage": "https://github.com/FlowNex-AI/gpt-image-2-mcp",
|
|
8
|
+
"repository": "https://github.com/FlowNex-AI/gpt-image-2-mcp",
|
|
9
|
+
"license": "MIT",
|
|
10
|
+
"keywords": ["openai", "gpt-image-2", "image-generation", "mcp", "cowork", "claude-code"],
|
|
11
|
+
"userConfig": {
|
|
12
|
+
"OPENAI_API_KEY": {
|
|
13
|
+
"type": "string",
|
|
14
|
+
"title": "OpenAI API key",
|
|
15
|
+
"description": "OpenAI API key with gpt-image-2 access. Get one at https://platform.openai.com/api-keys. Your organization must be verified to use the gpt-image family.",
|
|
16
|
+
"sensitive": true,
|
|
17
|
+
"required": true
|
|
18
|
+
},
|
|
19
|
+
"OPENAI_IMAGE_MODEL": {
|
|
20
|
+
"type": "string",
|
|
21
|
+
"title": "Model ID (optional)",
|
|
22
|
+
"description": "Override the model. Leave blank to use gpt-image-2.",
|
|
23
|
+
"default": "gpt-image-2",
|
|
24
|
+
"required": false
|
|
25
|
+
},
|
|
26
|
+
"MCP_GPT_IMAGE_2_OUTPUT_DIR": {
|
|
27
|
+
"type": "directory",
|
|
28
|
+
"title": "Output directory (optional)",
|
|
29
|
+
"description": "Where to save generated images. Defaults to ./generated_imgs in the current working directory.",
|
|
30
|
+
"required": false
|
|
31
|
+
},
|
|
32
|
+
"MCP_GPT_IMAGE_2_INLINE_IMAGE": {
|
|
33
|
+
"type": "string",
|
|
34
|
+
"title": "Return inline image data by default (optional)",
|
|
35
|
+
"description": "Set to \"false\" to skip embedding base64 image data in tool responses (saves context window). Defaults to \"true\".",
|
|
36
|
+
"default": "true",
|
|
37
|
+
"required": false
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
package/.mcp.json
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"mcpServers": {
|
|
3
|
+
"gpt-image-2": {
|
|
4
|
+
"command": "node",
|
|
5
|
+
"args": ["${CLAUDE_PLUGIN_ROOT}/dist/index.js"],
|
|
6
|
+
"env": {
|
|
7
|
+
"OPENAI_API_KEY": "${user_config.OPENAI_API_KEY}",
|
|
8
|
+
"OPENAI_IMAGE_MODEL": "${user_config.OPENAI_IMAGE_MODEL}",
|
|
9
|
+
"MCP_GPT_IMAGE_2_OUTPUT_DIR": "${user_config.MCP_GPT_IMAGE_2_OUTPUT_DIR}",
|
|
10
|
+
"MCP_GPT_IMAGE_2_INLINE_IMAGE": "${user_config.MCP_GPT_IMAGE_2_INLINE_IMAGE}"
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
}
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 daveremy
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
# mcp-gpt-image-2
|
|
2
|
+
|
|
3
|
+
MCP server for **OpenAI's `gpt-image-2`** image generation model. Distributed as a Claude plugin that works in **Claude Code** and **Claude Cowork**.
|
|
4
|
+
|
|
5
|
+
A fork of [daveremy/nano-banana-2-mcp](https://github.com/daveremy/nano-banana-2-mcp) (originally based on [ConechoAI/Nano-Banana-MCP](https://github.com/ConechoAI/Nano-Banana-MCP)), rewritten to target OpenAI's gpt-image-2 instead of Google Gemini.
|
|
6
|
+
|
|
7
|
+
Features:
|
|
8
|
+
|
|
9
|
+
- **gpt-image-2 model** — high-fidelity photorealism, ~99% text accuracy (incl. CJK), strong instruction following
|
|
10
|
+
- **Size control** — official presets (1024x1024, 1536x1024, 1024x1536, 2048x2048, auto) and custom `WxH` (multiples of 16, max edge 3840, ratio ≤ 3:1)
|
|
11
|
+
- **Quality modes** — `low`, `medium`, `high`, `auto`
|
|
12
|
+
- **Output formats** — PNG, JPEG, WebP
|
|
13
|
+
- **Multi-image edits** — up to 16 reference images per call, plus optional mask
|
|
14
|
+
- **Multiple variations** — generate 1–4 images per call (`n`)
|
|
15
|
+
- **File-path-only mode** — no inline base64, fixes context window overflow in Claude Code
|
|
16
|
+
- **Security hardening** — path validation, file size caps, no plaintext API key storage
|
|
17
|
+
|
|
18
|
+
## Setup
|
|
19
|
+
|
|
20
|
+
### 1. Get an OpenAI API key
|
|
21
|
+
|
|
22
|
+
Get one from [OpenAI Platform](https://platform.openai.com/api-keys). OpenAI gates the gpt-image family behind organization verification — complete it in the developer console if you hit a 403.
|
|
23
|
+
|
|
24
|
+
### 2. Install
|
|
25
|
+
|
|
26
|
+
### A) In Claude Code (recommended)
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
# 1. Add the marketplace (pulls .claude-plugin/marketplace.json from this repo)
|
|
30
|
+
claude plugin marketplace add FlowNex-AI/gpt-image-2-mcp
|
|
31
|
+
|
|
32
|
+
# 2. Install the plugin from that marketplace
|
|
33
|
+
claude plugin install gpt-image-2@mcp-gpt-image-2-plugins
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
The plugin prompts for your `OPENAI_API_KEY` on install (declared via `userConfig` in `plugin.json`, stored in your system keychain). The bundled MCP server (`dist/index.js`) runs from the cloned repo — no npm publish needed.
|
|
37
|
+
|
|
38
|
+
Alternative: inside Claude Code, run `/plugin` for an interactive picker.
|
|
39
|
+
|
|
40
|
+
### B) In Claude Cowork
|
|
41
|
+
|
|
42
|
+
Cowork reads the same plugin format:
|
|
43
|
+
|
|
44
|
+
1. Open Cowork → **Browse plugins** → **Upload custom plugin**, and point it at this repo (or its zipped release). Cowork picks up `.claude-plugin/plugin.json`, the bundled MCP server in `.mcp.json`, and the skills under `skills/`.
|
|
45
|
+
2. When prompted, paste your `OPENAI_API_KEY` (the same `userConfig` prompt as in Code).
|
|
46
|
+
3. The `generate-image` and `powerpoint-images` skills become available — try *"make me a hero image for slide 1 of my QBR deck"*.
|
|
47
|
+
|
|
48
|
+
For org-wide MDM deployments, drop this repo into the org-plugins directory documented in the Cowork enterprise admin guide.
|
|
49
|
+
|
|
50
|
+
### C) Manual MCP config (skip the plugin)
|
|
51
|
+
|
|
52
|
+
If you prefer to wire the MCP server directly into Claude Code, Cowork, or any MCP-compatible client (Cursor, Zed, etc.), add this to your MCP config:
|
|
53
|
+
|
|
54
|
+
```json
|
|
55
|
+
{
|
|
56
|
+
"mcpServers": {
|
|
57
|
+
"gpt-image-2": {
|
|
58
|
+
"command": "npx",
|
|
59
|
+
"args": ["-y", "@flownex-ai/mcp-gpt-image-2"],
|
|
60
|
+
"env": {
|
|
61
|
+
"OPENAI_API_KEY": "your-api-key-here"
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### D) From source (development)
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
git clone https://github.com/FlowNex-AI/gpt-image-2-mcp.git
|
|
72
|
+
cd gpt-image-2-mcp
|
|
73
|
+
npm install
|
|
74
|
+
npm run build
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Then point your MCP config at `dist/index.js`:
|
|
78
|
+
|
|
79
|
+
```json
|
|
80
|
+
{
|
|
81
|
+
"mcpServers": {
|
|
82
|
+
"gpt-image-2": {
|
|
83
|
+
"command": "node",
|
|
84
|
+
"args": ["/path/to/gpt-image-2-mcp/dist/index.js"],
|
|
85
|
+
"env": {
|
|
86
|
+
"OPENAI_API_KEY": "your-api-key-here"
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### 3. Restart Claude Code
|
|
94
|
+
|
|
95
|
+
The tools will be available after restart.
|
|
96
|
+
|
|
97
|
+
## Tools
|
|
98
|
+
|
|
99
|
+
### `generate_image`
|
|
100
|
+
|
|
101
|
+
Generate a new image from a text prompt.
|
|
102
|
+
|
|
103
|
+
| Parameter | Type | Default | Description |
|
|
104
|
+
|-----------|------|---------|-------------|
|
|
105
|
+
| `prompt` | string | (required) | Text prompt for the image |
|
|
106
|
+
| `size` | string | `"1024x1024"` | Preset (`1024x1024`, `1536x1024`, `1024x1536`, `2048x2048`, `auto`) or custom `WxH` |
|
|
107
|
+
| `quality` | string | `"auto"` | `low`, `medium`, `high`, or `auto` |
|
|
108
|
+
| `numberOfImages` | number | `1` | 1–4 |
|
|
109
|
+
| `outputFormat` | string | `"png"` | `png`, `jpeg`, or `webp` |
|
|
110
|
+
| `background` | string | `"auto"` | `auto` or `opaque` (transparent not supported) |
|
|
111
|
+
| `returnInlineImage` | boolean | `true` | If false, return only file path |
|
|
112
|
+
|
|
113
|
+
### `edit_image`
|
|
114
|
+
|
|
115
|
+
Edit an existing image file. Same parameters as `generate_image`, plus:
|
|
116
|
+
|
|
117
|
+
| Parameter | Type | Description |
|
|
118
|
+
|-----------|------|-------------|
|
|
119
|
+
| `imagePath` | string | (required) Path to the image to edit |
|
|
120
|
+
| `referenceImages` | string[] | Optional additional reference images (up to 15 more, 16 total) |
|
|
121
|
+
| `mask` | string | Optional PNG mask path; transparent pixels mark the editable area |
|
|
122
|
+
|
|
123
|
+
### `continue_editing`
|
|
124
|
+
|
|
125
|
+
Continue editing the last generated/edited image. Same parameters as `edit_image` minus `imagePath` (uses the last image automatically).
|
|
126
|
+
|
|
127
|
+
### `get_configuration_status`
|
|
128
|
+
|
|
129
|
+
Check API key, active model, and settings.
|
|
130
|
+
|
|
131
|
+
### `get_last_image_info`
|
|
132
|
+
|
|
133
|
+
Get path and size of the last generated image.
|
|
134
|
+
|
|
135
|
+
## Environment Variables
|
|
136
|
+
|
|
137
|
+
| Variable | Default | Description |
|
|
138
|
+
|----------|---------|-------------|
|
|
139
|
+
| `OPENAI_API_KEY` | (required) | OpenAI API key |
|
|
140
|
+
| `OPENAI_IMAGE_MODEL` | `gpt-image-2` | Model ID override (e.g. `gpt-image-2-2026-04-21`, `gpt-image-1`) |
|
|
141
|
+
| `MCP_GPT_IMAGE_2_OUTPUT_DIR` | `./generated_imgs` | Image save directory |
|
|
142
|
+
| `MCP_GPT_IMAGE_2_INLINE_IMAGE` | `true` | Default for `returnInlineImage` |
|
|
143
|
+
|
|
144
|
+
## Size Constraints
|
|
145
|
+
|
|
146
|
+
When using a custom `WxH` size, gpt-image-2 requires:
|
|
147
|
+
|
|
148
|
+
- Both edges are multiples of **16**
|
|
149
|
+
- Max single edge: **3840px**
|
|
150
|
+
- Total pixels: **655,360 – 8,294,400**
|
|
151
|
+
- Long-edge to short-edge ratio: **≤ 3:1**
|
|
152
|
+
|
|
153
|
+
The server validates these before calling the API.
|
|
154
|
+
|
|
155
|
+
## Plugin (Claude Code + Cowork)
|
|
156
|
+
|
|
157
|
+
This repo includes a unified Claude plugin that works in both **Claude Code** and **Claude Cowork** (same `.claude-plugin/plugin.json` schema). It ships:
|
|
158
|
+
|
|
159
|
+
- An MCP server (`.mcp.json` declares the `gpt-image-2` server, bundled `dist/index.js`)
|
|
160
|
+
- A `userConfig` block that prompts for `OPENAI_API_KEY` on install (stored in the system keychain, not plain text)
|
|
161
|
+
- Two skills:
|
|
162
|
+
- `generate-image` — best-practice prompting for general image generation
|
|
163
|
+
- `powerpoint-images` — guidance tailored to PowerPoint / Keynote / Google Slides decks (a natural fit for Cowork knowledge workers)
|
|
164
|
+
|
|
165
|
+
See the install instructions above for both Claude Code and Cowork.
|
|
166
|
+
|
|
167
|
+
## Contributing
|
|
168
|
+
|
|
169
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup, testing, and release process.
|
|
170
|
+
|
|
171
|
+
## Attribution
|
|
172
|
+
|
|
173
|
+
Based on [daveremy/nano-banana-2-mcp](https://github.com/daveremy/nano-banana-2-mcp), which is based on [ConechoAI/Nano-Banana-MCP](https://github.com/ConechoAI/Nano-Banana-MCP) (MIT License).
|
|
174
|
+
|
|
175
|
+
## License
|
|
176
|
+
|
|
177
|
+
MIT
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,522 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
3
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
|
+
import { CallToolRequestSchema, ListToolsRequestSchema, ErrorCode, McpError, } from "@modelcontextprotocol/sdk/types.js";
|
|
5
|
+
import OpenAI, { toFile } from "openai";
|
|
6
|
+
import fs from "fs/promises";
|
|
7
|
+
import { createReadStream } from "fs";
|
|
8
|
+
import path from "path";
|
|
9
|
+
import { VERSION } from "./version.js";
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
// Constants
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
const DEFAULT_MODEL = "gpt-image-2";
|
|
14
|
+
const SIZE_PRESETS = new Set([
|
|
15
|
+
"1024x1024",
|
|
16
|
+
"1536x1024",
|
|
17
|
+
"1024x1536",
|
|
18
|
+
"2048x2048",
|
|
19
|
+
"auto",
|
|
20
|
+
]);
|
|
21
|
+
const SIZE_PATTERN = /^(\d+)x(\d+)$/;
|
|
22
|
+
const MIN_TOTAL_PIXELS = 655_360;
|
|
23
|
+
const MAX_TOTAL_PIXELS = 8_294_400;
|
|
24
|
+
const MAX_EDGE_PX = 3840;
|
|
25
|
+
const VALID_QUALITY = new Set(["low", "medium", "high", "auto"]);
|
|
26
|
+
const VALID_OUTPUT_FORMATS = new Set(["png", "jpeg", "webp"]);
|
|
27
|
+
const VALID_BACKGROUNDS = new Set(["opaque", "auto"]);
|
|
28
|
+
const ALLOWED_IMAGE_EXTENSIONS = new Set([".png", ".jpg", ".jpeg", ".webp"]);
|
|
29
|
+
const MAX_IMAGE_FILE_SIZE = 50 * 1024 * 1024; // 50 MB
|
|
30
|
+
const MAX_NUMBER_OF_IMAGES = 4;
|
|
31
|
+
const MAX_REFERENCE_IMAGES = 16;
|
|
32
|
+
// ---------------------------------------------------------------------------
|
|
33
|
+
// Helpers
|
|
34
|
+
// ---------------------------------------------------------------------------
|
|
35
|
+
function getModelId() {
|
|
36
|
+
return process.env.OPENAI_IMAGE_MODEL || DEFAULT_MODEL;
|
|
37
|
+
}
|
|
38
|
+
function getOutputDir() {
|
|
39
|
+
return process.env.MCP_GPT_IMAGE_2_OUTPUT_DIR || path.join(process.cwd(), "generated_imgs");
|
|
40
|
+
}
|
|
41
|
+
function resolveInlineImage(perCall) {
|
|
42
|
+
if (perCall !== undefined)
|
|
43
|
+
return perCall;
|
|
44
|
+
const env = process.env.MCP_GPT_IMAGE_2_INLINE_IMAGE;
|
|
45
|
+
if (env !== undefined)
|
|
46
|
+
return env === "true";
|
|
47
|
+
return true; // default
|
|
48
|
+
}
|
|
49
|
+
function extensionToMime(ext) {
|
|
50
|
+
if (ext === ".webp")
|
|
51
|
+
return "image/webp";
|
|
52
|
+
if (ext === ".png")
|
|
53
|
+
return "image/png";
|
|
54
|
+
return "image/jpeg";
|
|
55
|
+
}
|
|
56
|
+
function outputFormatToMime(format) {
|
|
57
|
+
if (format === "jpeg")
|
|
58
|
+
return "image/jpeg";
|
|
59
|
+
if (format === "webp")
|
|
60
|
+
return "image/webp";
|
|
61
|
+
return "image/png";
|
|
62
|
+
}
|
|
63
|
+
function randomId() {
|
|
64
|
+
return Math.random().toString(36).slice(2, 8);
|
|
65
|
+
}
|
|
66
|
+
function timestamp() {
|
|
67
|
+
return new Date().toISOString().replace(/[:.]/g, "-");
|
|
68
|
+
}
|
|
69
|
+
function formatBytes(bytes) {
|
|
70
|
+
if (bytes < 1024)
|
|
71
|
+
return `${bytes} B`;
|
|
72
|
+
if (bytes < 1024 * 1024)
|
|
73
|
+
return `${(bytes / 1024).toFixed(1)} KB`;
|
|
74
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
75
|
+
}
|
|
76
|
+
function validateSize(size) {
|
|
77
|
+
if (SIZE_PRESETS.has(size))
|
|
78
|
+
return;
|
|
79
|
+
const match = SIZE_PATTERN.exec(size);
|
|
80
|
+
if (!match) {
|
|
81
|
+
throw new McpError(ErrorCode.InvalidParams, `Invalid size "${size}". Use a preset (${[...SIZE_PRESETS].join(", ")}) or custom WxH like "1280x720".`);
|
|
82
|
+
}
|
|
83
|
+
const w = Number(match[1]);
|
|
84
|
+
const h = Number(match[2]);
|
|
85
|
+
if (w % 16 !== 0 || h % 16 !== 0) {
|
|
86
|
+
throw new McpError(ErrorCode.InvalidParams, `Custom size "${size}" must have both edges as multiples of 16.`);
|
|
87
|
+
}
|
|
88
|
+
if (w > MAX_EDGE_PX || h > MAX_EDGE_PX) {
|
|
89
|
+
throw new McpError(ErrorCode.InvalidParams, `Custom size "${size}" max edge is ${MAX_EDGE_PX}px.`);
|
|
90
|
+
}
|
|
91
|
+
const total = w * h;
|
|
92
|
+
if (total < MIN_TOTAL_PIXELS || total > MAX_TOTAL_PIXELS) {
|
|
93
|
+
throw new McpError(ErrorCode.InvalidParams, `Custom size "${size}" total pixels (${total}) must be between ${MIN_TOTAL_PIXELS} and ${MAX_TOTAL_PIXELS}.`);
|
|
94
|
+
}
|
|
95
|
+
const ratio = Math.max(w, h) / Math.min(w, h);
|
|
96
|
+
if (ratio > 3) {
|
|
97
|
+
throw new McpError(ErrorCode.InvalidParams, `Custom size "${size}" long-edge to short-edge ratio (${ratio.toFixed(2)}) must not exceed 3:1.`);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
async function validateImagePath(filePath) {
|
|
101
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
102
|
+
if (!ALLOWED_IMAGE_EXTENSIONS.has(ext)) {
|
|
103
|
+
throw new McpError(ErrorCode.InvalidParams, `Invalid image extension "${ext}". Allowed: ${[...ALLOWED_IMAGE_EXTENSIONS].join(", ")}`);
|
|
104
|
+
}
|
|
105
|
+
let stat;
|
|
106
|
+
try {
|
|
107
|
+
stat = await fs.stat(filePath);
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
throw new McpError(ErrorCode.InvalidParams, `File not found: ${filePath}`);
|
|
111
|
+
}
|
|
112
|
+
if (!stat.isFile()) {
|
|
113
|
+
throw new McpError(ErrorCode.InvalidParams, `Not a file: ${filePath}`);
|
|
114
|
+
}
|
|
115
|
+
if (stat.size > MAX_IMAGE_FILE_SIZE) {
|
|
116
|
+
throw new McpError(ErrorCode.InvalidParams, `File too large (${formatBytes(stat.size)}). Max: ${formatBytes(MAX_IMAGE_FILE_SIZE)}`);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
// ---------------------------------------------------------------------------
|
|
120
|
+
// Common image generation parameters (shared schema)
|
|
121
|
+
// ---------------------------------------------------------------------------
|
|
122
|
+
const imageParamProperties = {
|
|
123
|
+
size: {
|
|
124
|
+
type: "string",
|
|
125
|
+
description: "Image dimensions. Presets: \"1024x1024\" (square), \"1536x1024\" (landscape), \"1024x1536\" (portrait), \"2048x2048\" (square hi-res), \"auto\". Custom \"WxH\" also accepted: edges multiples of 16, max edge 3840px, total pixels 655,360–8,294,400, ratio ≤ 3:1.",
|
|
126
|
+
default: "1024x1024",
|
|
127
|
+
},
|
|
128
|
+
quality: {
|
|
129
|
+
type: "string",
|
|
130
|
+
description: "Rendering quality: \"low\", \"medium\", \"high\", or \"auto\". Higher quality increases latency and cost.",
|
|
131
|
+
default: "auto",
|
|
132
|
+
},
|
|
133
|
+
numberOfImages: {
|
|
134
|
+
type: "number",
|
|
135
|
+
description: "Number of images to generate (1–4).",
|
|
136
|
+
default: 1,
|
|
137
|
+
},
|
|
138
|
+
outputFormat: {
|
|
139
|
+
type: "string",
|
|
140
|
+
description: "Output file format: \"png\" (default), \"jpeg\", or \"webp\".",
|
|
141
|
+
default: "png",
|
|
142
|
+
},
|
|
143
|
+
background: {
|
|
144
|
+
type: "string",
|
|
145
|
+
description: "Background handling: \"opaque\" or \"auto\". Transparent backgrounds are not supported by gpt-image-2.",
|
|
146
|
+
default: "auto",
|
|
147
|
+
},
|
|
148
|
+
returnInlineImage: {
|
|
149
|
+
type: "boolean",
|
|
150
|
+
description: "If false, return only file path (no base64). Saves context window space.",
|
|
151
|
+
},
|
|
152
|
+
};
|
|
153
|
+
// ---------------------------------------------------------------------------
|
|
154
|
+
// Server
|
|
155
|
+
// ---------------------------------------------------------------------------
|
|
156
|
+
class GptImage2MCP {
|
|
157
|
+
server;
|
|
158
|
+
openai = null;
|
|
159
|
+
lastImagePath = null;
|
|
160
|
+
constructor() {
|
|
161
|
+
this.server = new Server({ name: "gpt-image-2", version: VERSION }, { capabilities: { tools: {} } });
|
|
162
|
+
this.setupHandlers();
|
|
163
|
+
}
|
|
164
|
+
// -------------------------------------------------------------------------
|
|
165
|
+
// Init
|
|
166
|
+
// -------------------------------------------------------------------------
|
|
167
|
+
initOpenAI() {
|
|
168
|
+
if (this.openai)
|
|
169
|
+
return this.openai;
|
|
170
|
+
const apiKey = process.env.OPENAI_API_KEY;
|
|
171
|
+
if (!apiKey) {
|
|
172
|
+
throw new McpError(ErrorCode.InvalidRequest, "OPENAI_API_KEY environment variable is required. Set it in your MCP server config.");
|
|
173
|
+
}
|
|
174
|
+
this.openai = new OpenAI({ apiKey });
|
|
175
|
+
return this.openai;
|
|
176
|
+
}
|
|
177
|
+
// -------------------------------------------------------------------------
|
|
178
|
+
// Tool definitions
|
|
179
|
+
// -------------------------------------------------------------------------
|
|
180
|
+
setupHandlers() {
|
|
181
|
+
this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
182
|
+
tools: [
|
|
183
|
+
{
|
|
184
|
+
name: "generate_image",
|
|
185
|
+
description: "Generate a NEW image from text prompt using OpenAI gpt-image-2. Use this ONLY when creating a completely new image, not when modifying an existing one.",
|
|
186
|
+
inputSchema: {
|
|
187
|
+
type: "object",
|
|
188
|
+
properties: {
|
|
189
|
+
prompt: {
|
|
190
|
+
type: "string",
|
|
191
|
+
description: "Text prompt describing the NEW image to create from scratch",
|
|
192
|
+
},
|
|
193
|
+
...imageParamProperties,
|
|
194
|
+
},
|
|
195
|
+
required: ["prompt"],
|
|
196
|
+
},
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
name: "edit_image",
|
|
200
|
+
description: "Edit a SPECIFIC existing image file with OpenAI gpt-image-2, optionally using additional reference images. Use this when you have the exact file path of an image to modify.",
|
|
201
|
+
inputSchema: {
|
|
202
|
+
type: "object",
|
|
203
|
+
properties: {
|
|
204
|
+
imagePath: {
|
|
205
|
+
type: "string",
|
|
206
|
+
description: "Full file path to the main image file to edit",
|
|
207
|
+
},
|
|
208
|
+
prompt: {
|
|
209
|
+
type: "string",
|
|
210
|
+
description: "Text describing the modifications to make to the existing image",
|
|
211
|
+
},
|
|
212
|
+
referenceImages: {
|
|
213
|
+
type: "array",
|
|
214
|
+
items: { type: "string" },
|
|
215
|
+
description: "Optional array of file paths to additional reference images (up to 15 in addition to the main image, 16 total)",
|
|
216
|
+
},
|
|
217
|
+
mask: {
|
|
218
|
+
type: "string",
|
|
219
|
+
description: "Optional file path to a PNG mask. Transparent pixels indicate the area to edit; must match the main image dimensions.",
|
|
220
|
+
},
|
|
221
|
+
...imageParamProperties,
|
|
222
|
+
},
|
|
223
|
+
required: ["imagePath", "prompt"],
|
|
224
|
+
},
|
|
225
|
+
},
|
|
226
|
+
{
|
|
227
|
+
name: "continue_editing",
|
|
228
|
+
description: "Continue editing the LAST image that was generated or edited in this session, optionally using additional reference images.",
|
|
229
|
+
inputSchema: {
|
|
230
|
+
type: "object",
|
|
231
|
+
properties: {
|
|
232
|
+
prompt: {
|
|
233
|
+
type: "string",
|
|
234
|
+
description: "Text describing the modifications to the last image",
|
|
235
|
+
},
|
|
236
|
+
referenceImages: {
|
|
237
|
+
type: "array",
|
|
238
|
+
items: { type: "string" },
|
|
239
|
+
description: "Optional array of file paths to additional reference images",
|
|
240
|
+
},
|
|
241
|
+
mask: {
|
|
242
|
+
type: "string",
|
|
243
|
+
description: "Optional file path to a PNG mask matching the last image dimensions",
|
|
244
|
+
},
|
|
245
|
+
...imageParamProperties,
|
|
246
|
+
},
|
|
247
|
+
required: ["prompt"],
|
|
248
|
+
},
|
|
249
|
+
},
|
|
250
|
+
{
|
|
251
|
+
name: "get_configuration_status",
|
|
252
|
+
description: "Check if OpenAI API key is configured and which model is active",
|
|
253
|
+
inputSchema: { type: "object", properties: {}, additionalProperties: false },
|
|
254
|
+
},
|
|
255
|
+
{
|
|
256
|
+
name: "get_last_image_info",
|
|
257
|
+
description: "Get information about the last generated/edited image in this session",
|
|
258
|
+
inputSchema: { type: "object", properties: {}, additionalProperties: false },
|
|
259
|
+
},
|
|
260
|
+
],
|
|
261
|
+
}));
|
|
262
|
+
this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
263
|
+
try {
|
|
264
|
+
switch (request.params.name) {
|
|
265
|
+
case "generate_image":
|
|
266
|
+
return await this.generateImage(request);
|
|
267
|
+
case "edit_image":
|
|
268
|
+
return await this.editImage(request);
|
|
269
|
+
case "continue_editing":
|
|
270
|
+
return await this.continueEditing(request);
|
|
271
|
+
case "get_configuration_status":
|
|
272
|
+
return this.getConfigurationStatus();
|
|
273
|
+
case "get_last_image_info":
|
|
274
|
+
return await this.getLastImageInfo();
|
|
275
|
+
default:
|
|
276
|
+
throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${request.params.name}`);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
catch (error) {
|
|
280
|
+
if (error instanceof McpError)
|
|
281
|
+
throw error;
|
|
282
|
+
throw new McpError(ErrorCode.InternalError, `Tool execution failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
283
|
+
}
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
// -------------------------------------------------------------------------
|
|
287
|
+
// Parameter extraction
|
|
288
|
+
// -------------------------------------------------------------------------
|
|
289
|
+
extractImageParams(args) {
|
|
290
|
+
const size = args.size || "1024x1024";
|
|
291
|
+
const quality = args.quality || "auto";
|
|
292
|
+
const outputFormat = (args.outputFormat || "png").toLowerCase();
|
|
293
|
+
const background = args.background || "auto";
|
|
294
|
+
const raw = Math.round(Number(args.numberOfImages) || 1);
|
|
295
|
+
const numberOfImages = Math.min(Math.max(raw, 1), MAX_NUMBER_OF_IMAGES);
|
|
296
|
+
const returnInlineImage = resolveInlineImage(args.returnInlineImage === undefined ? undefined : Boolean(args.returnInlineImage));
|
|
297
|
+
validateSize(size);
|
|
298
|
+
if (!VALID_QUALITY.has(quality)) {
|
|
299
|
+
throw new McpError(ErrorCode.InvalidParams, `Invalid quality "${quality}". Use: ${[...VALID_QUALITY].join(", ")}`);
|
|
300
|
+
}
|
|
301
|
+
if (!VALID_OUTPUT_FORMATS.has(outputFormat)) {
|
|
302
|
+
throw new McpError(ErrorCode.InvalidParams, `Invalid outputFormat "${outputFormat}". Use: ${[...VALID_OUTPUT_FORMATS].join(", ")}`);
|
|
303
|
+
}
|
|
304
|
+
if (!VALID_BACKGROUNDS.has(background)) {
|
|
305
|
+
throw new McpError(ErrorCode.InvalidParams, `Invalid background "${background}". Use: ${[...VALID_BACKGROUNDS].join(", ")}`);
|
|
306
|
+
}
|
|
307
|
+
return { size, quality, numberOfImages, outputFormat, background, returnInlineImage };
|
|
308
|
+
}
|
|
309
|
+
// -------------------------------------------------------------------------
|
|
310
|
+
// Image saving
|
|
311
|
+
// -------------------------------------------------------------------------
|
|
312
|
+
async ensureOutputDir() {
|
|
313
|
+
const dir = getOutputDir();
|
|
314
|
+
await fs.mkdir(dir, { recursive: true });
|
|
315
|
+
return dir;
|
|
316
|
+
}
|
|
317
|
+
async saveImage(base64Data, mimeType, prefix, suffix) {
|
|
318
|
+
const dir = await this.ensureOutputDir();
|
|
319
|
+
const ext = mimeType === "image/jpeg" ? ".jpg" : mimeType === "image/webp" ? ".webp" : ".png";
|
|
320
|
+
const name = `${prefix}-${timestamp()}-${randomId()}${suffix || ""}${ext}`;
|
|
321
|
+
const filePath = path.join(dir, name);
|
|
322
|
+
const buffer = Buffer.from(base64Data, "base64");
|
|
323
|
+
await fs.writeFile(filePath, buffer);
|
|
324
|
+
return { filePath, fileSize: buffer.length };
|
|
325
|
+
}
|
|
326
|
+
// -------------------------------------------------------------------------
|
|
327
|
+
// Response building
|
|
328
|
+
// -------------------------------------------------------------------------
|
|
329
|
+
buildResponse(savedImages, returnInlineImage) {
|
|
330
|
+
const content = [];
|
|
331
|
+
const lines = savedImages.map((img) => `${img.filePath} (${formatBytes(img.fileSize)})`);
|
|
332
|
+
const target = savedImages.length === 1 ? "this image" : "the first image";
|
|
333
|
+
lines.push(`Use continue_editing to refine ${target}.`);
|
|
334
|
+
content.push({ type: "text", text: lines.join("\n") });
|
|
335
|
+
if (returnInlineImage) {
|
|
336
|
+
for (const img of savedImages) {
|
|
337
|
+
content.push({
|
|
338
|
+
type: "image",
|
|
339
|
+
data: img.base64,
|
|
340
|
+
mimeType: img.mimeType,
|
|
341
|
+
});
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
return { content };
|
|
345
|
+
}
|
|
346
|
+
// -------------------------------------------------------------------------
|
|
347
|
+
// Tools: generate_image
|
|
348
|
+
// -------------------------------------------------------------------------
|
|
349
|
+
async generateImage(request) {
|
|
350
|
+
const args = request.params.arguments;
|
|
351
|
+
const prompt = args.prompt;
|
|
352
|
+
if (!prompt)
|
|
353
|
+
throw new McpError(ErrorCode.InvalidParams, "prompt is required");
|
|
354
|
+
const params = this.extractImageParams(args);
|
|
355
|
+
const openai = this.initOpenAI();
|
|
356
|
+
const modelId = getModelId();
|
|
357
|
+
const generateRequest = {
|
|
358
|
+
model: modelId,
|
|
359
|
+
prompt,
|
|
360
|
+
size: params.size,
|
|
361
|
+
quality: params.quality,
|
|
362
|
+
n: params.numberOfImages,
|
|
363
|
+
output_format: params.outputFormat,
|
|
364
|
+
background: params.background,
|
|
365
|
+
};
|
|
366
|
+
const response = await openai.images.generate(generateRequest);
|
|
367
|
+
const allSaved = await this.processResponse(response, "generated", params);
|
|
368
|
+
if (allSaved.length === 0) {
|
|
369
|
+
return { content: [{ type: "text", text: "No image was generated. Try rephrasing your prompt." }] };
|
|
370
|
+
}
|
|
371
|
+
this.lastImagePath = allSaved[0].filePath;
|
|
372
|
+
return this.buildResponse(allSaved, params.returnInlineImage);
|
|
373
|
+
}
|
|
374
|
+
// -------------------------------------------------------------------------
|
|
375
|
+
// Tools: edit_image
|
|
376
|
+
// -------------------------------------------------------------------------
|
|
377
|
+
async editImage(request) {
|
|
378
|
+
const args = request.params.arguments;
|
|
379
|
+
const imagePath = args.imagePath;
|
|
380
|
+
const prompt = args.prompt;
|
|
381
|
+
const referenceImages = args.referenceImages || [];
|
|
382
|
+
const maskPath = args.mask;
|
|
383
|
+
if (!imagePath)
|
|
384
|
+
throw new McpError(ErrorCode.InvalidParams, "imagePath is required");
|
|
385
|
+
if (!prompt)
|
|
386
|
+
throw new McpError(ErrorCode.InvalidParams, "prompt is required");
|
|
387
|
+
const allPaths = [imagePath, ...referenceImages];
|
|
388
|
+
if (allPaths.length > MAX_REFERENCE_IMAGES) {
|
|
389
|
+
throw new McpError(ErrorCode.InvalidParams, `Too many images (${allPaths.length}). Max: ${MAX_REFERENCE_IMAGES} total (main + references).`);
|
|
390
|
+
}
|
|
391
|
+
for (const p of allPaths) {
|
|
392
|
+
await validateImagePath(p);
|
|
393
|
+
}
|
|
394
|
+
if (maskPath)
|
|
395
|
+
await validateImagePath(maskPath);
|
|
396
|
+
const params = this.extractImageParams(args);
|
|
397
|
+
const openai = this.initOpenAI();
|
|
398
|
+
const modelId = getModelId();
|
|
399
|
+
const imageFiles = await Promise.all(allPaths.map(async (p) => {
|
|
400
|
+
const ext = path.extname(p).toLowerCase();
|
|
401
|
+
const mime = extensionToMime(ext);
|
|
402
|
+
return await toFile(createReadStream(p), path.basename(p), { type: mime });
|
|
403
|
+
}));
|
|
404
|
+
const editRequest = {
|
|
405
|
+
model: modelId,
|
|
406
|
+
image: imageFiles,
|
|
407
|
+
prompt,
|
|
408
|
+
size: params.size,
|
|
409
|
+
quality: params.quality,
|
|
410
|
+
n: params.numberOfImages,
|
|
411
|
+
output_format: params.outputFormat,
|
|
412
|
+
background: params.background,
|
|
413
|
+
};
|
|
414
|
+
if (maskPath) {
|
|
415
|
+
const maskMime = extensionToMime(path.extname(maskPath).toLowerCase());
|
|
416
|
+
editRequest.mask = await toFile(createReadStream(maskPath), path.basename(maskPath), {
|
|
417
|
+
type: maskMime,
|
|
418
|
+
});
|
|
419
|
+
}
|
|
420
|
+
const response = await openai.images.edit(editRequest);
|
|
421
|
+
const saved = await this.processResponse(response, "edited", params);
|
|
422
|
+
if (saved.length === 0) {
|
|
423
|
+
return { content: [{ type: "text", text: "No edited image was produced. Try a different prompt." }] };
|
|
424
|
+
}
|
|
425
|
+
this.lastImagePath = saved[0].filePath;
|
|
426
|
+
return this.buildResponse(saved, params.returnInlineImage);
|
|
427
|
+
}
|
|
428
|
+
// -------------------------------------------------------------------------
|
|
429
|
+
// Tools: continue_editing
|
|
430
|
+
// -------------------------------------------------------------------------
|
|
431
|
+
async continueEditing(request) {
|
|
432
|
+
if (!this.lastImagePath) {
|
|
433
|
+
throw new McpError(ErrorCode.InvalidRequest, "No previous image in this session. Use generate_image or edit_image first.");
|
|
434
|
+
}
|
|
435
|
+
const args = request.params.arguments;
|
|
436
|
+
const editArgs = { ...args, imagePath: this.lastImagePath };
|
|
437
|
+
const editRequest = {
|
|
438
|
+
...request,
|
|
439
|
+
params: { ...request.params, arguments: editArgs },
|
|
440
|
+
};
|
|
441
|
+
return this.editImage(editRequest);
|
|
442
|
+
}
|
|
443
|
+
// -------------------------------------------------------------------------
|
|
444
|
+
// Tools: get_configuration_status
|
|
445
|
+
// -------------------------------------------------------------------------
|
|
446
|
+
getConfigurationStatus() {
|
|
447
|
+
const hasKey = !!process.env.OPENAI_API_KEY;
|
|
448
|
+
const modelId = getModelId();
|
|
449
|
+
const lines = [
|
|
450
|
+
`API key: ${hasKey ? "configured" : "NOT configured — set OPENAI_API_KEY in MCP server env"}`,
|
|
451
|
+
`Model: ${modelId}`,
|
|
452
|
+
`Output dir: ${getOutputDir()}`,
|
|
453
|
+
`Inline images: ${resolveInlineImage(undefined)}`,
|
|
454
|
+
];
|
|
455
|
+
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
456
|
+
}
|
|
457
|
+
// -------------------------------------------------------------------------
|
|
458
|
+
// Tools: get_last_image_info
|
|
459
|
+
// -------------------------------------------------------------------------
|
|
460
|
+
async getLastImageInfo() {
|
|
461
|
+
if (!this.lastImagePath) {
|
|
462
|
+
return { content: [{ type: "text", text: "No image generated in this session yet." }] };
|
|
463
|
+
}
|
|
464
|
+
try {
|
|
465
|
+
const stat = await fs.stat(this.lastImagePath);
|
|
466
|
+
return {
|
|
467
|
+
content: [
|
|
468
|
+
{
|
|
469
|
+
type: "text",
|
|
470
|
+
text: `Last image: ${this.lastImagePath}\nSize: ${formatBytes(stat.size)}`,
|
|
471
|
+
},
|
|
472
|
+
],
|
|
473
|
+
};
|
|
474
|
+
}
|
|
475
|
+
catch {
|
|
476
|
+
return {
|
|
477
|
+
content: [
|
|
478
|
+
{
|
|
479
|
+
type: "text",
|
|
480
|
+
text: `Last image path recorded: ${this.lastImagePath}\n(File may have been moved or deleted)`,
|
|
481
|
+
},
|
|
482
|
+
],
|
|
483
|
+
};
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
// -------------------------------------------------------------------------
|
|
487
|
+
// Process OpenAI response → saved images
|
|
488
|
+
// -------------------------------------------------------------------------
|
|
489
|
+
async processResponse(response, prefix, params) {
|
|
490
|
+
const items = response.data || [];
|
|
491
|
+
const mimeType = outputFormatToMime(params.outputFormat);
|
|
492
|
+
const saved = [];
|
|
493
|
+
for (let i = 0; i < items.length; i++) {
|
|
494
|
+
const item = items[i];
|
|
495
|
+
let base64 = item.b64_json;
|
|
496
|
+
if (!base64 && item.url) {
|
|
497
|
+
const res = await fetch(item.url);
|
|
498
|
+
if (!res.ok) {
|
|
499
|
+
throw new McpError(ErrorCode.InternalError, `Failed to download image from ${item.url}`);
|
|
500
|
+
}
|
|
501
|
+
const buf = Buffer.from(await res.arrayBuffer());
|
|
502
|
+
base64 = buf.toString("base64");
|
|
503
|
+
}
|
|
504
|
+
if (!base64)
|
|
505
|
+
continue;
|
|
506
|
+
const suffix = items.length > 1 ? `-${i + 1}` : "";
|
|
507
|
+
const { filePath, fileSize } = await this.saveImage(base64, mimeType, prefix, suffix);
|
|
508
|
+
saved.push({ filePath, fileSize, base64, mimeType });
|
|
509
|
+
}
|
|
510
|
+
return saved;
|
|
511
|
+
}
|
|
512
|
+
// -------------------------------------------------------------------------
|
|
513
|
+
// Run
|
|
514
|
+
// -------------------------------------------------------------------------
|
|
515
|
+
async run() {
|
|
516
|
+
const transport = new StdioServerTransport();
|
|
517
|
+
await this.server.connect(transport);
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
const server = new GptImage2MCP();
|
|
521
|
+
server.run().catch(console.error);
|
|
522
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const VERSION = "0.1.0";
|
package/dist/version.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"version.js","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,OAAO,GAAG,OAAO,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@flownex-ai/mcp-gpt-image-2",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "MCP server for OpenAI's gpt-image-2 image generation with size/quality control, multi-image support, and context-window-safe output",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"bin": {
|
|
8
|
+
"mcp-gpt-image-2": "dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"publishConfig": {
|
|
11
|
+
"access": "public"
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"dist/",
|
|
15
|
+
".claude-plugin/",
|
|
16
|
+
".mcp.json",
|
|
17
|
+
"skills/",
|
|
18
|
+
"README.md",
|
|
19
|
+
"LICENSE"
|
|
20
|
+
],
|
|
21
|
+
"scripts": {
|
|
22
|
+
"build": "tsc",
|
|
23
|
+
"prepack": "npm run build",
|
|
24
|
+
"start": "node dist/index.js",
|
|
25
|
+
"dev": "tsx src/index.ts",
|
|
26
|
+
"test": "node --import tsx --test test/*.test.ts",
|
|
27
|
+
"release": "./scripts/release.sh"
|
|
28
|
+
},
|
|
29
|
+
"engines": {
|
|
30
|
+
"node": ">=18.0.0"
|
|
31
|
+
},
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
34
|
+
"openai": "^4.79.0"
|
|
35
|
+
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"@types/node": "^20.11.0",
|
|
38
|
+
"tsx": "^4.7.0",
|
|
39
|
+
"typescript": "^5.3.3"
|
|
40
|
+
},
|
|
41
|
+
"repository": {
|
|
42
|
+
"type": "git",
|
|
43
|
+
"url": "git+https://github.com/FlowNex-AI/gpt-image-2-mcp.git"
|
|
44
|
+
},
|
|
45
|
+
"keywords": [
|
|
46
|
+
"mcp",
|
|
47
|
+
"openai",
|
|
48
|
+
"gpt-image-2",
|
|
49
|
+
"image-generation",
|
|
50
|
+
"claude-code"
|
|
51
|
+
],
|
|
52
|
+
"license": "MIT",
|
|
53
|
+
"author": {
|
|
54
|
+
"name": "FlowNex AI",
|
|
55
|
+
"url": "https://flownexai.com"
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: generate-image
|
|
3
|
+
description: Generate or edit images using OpenAI gpt-image-2. Use this skill any time the user asks to create, generate, illustrate, design, mock up, or edit an image — from product shots and diagrams to marketing visuals and slide imagery. Works in Claude Code and Claude Cowork.
|
|
4
|
+
allowed-tools: mcp__gpt-image-2__generate_image, mcp__gpt-image-2__edit_image, mcp__gpt-image-2__continue_editing, mcp__gpt-image-2__get_configuration_status, mcp__gpt-image-2__get_last_image_info
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Image Generation with OpenAI gpt-image-2
|
|
8
|
+
|
|
9
|
+
Use the `generate_image`, `edit_image`, and `continue_editing` MCP tools from the `gpt-image-2` server (powered by OpenAI's `gpt-image-2` model).
|
|
10
|
+
|
|
11
|
+
## First-Time Setup
|
|
12
|
+
|
|
13
|
+
If you aren't sure the API key is configured, call `get_configuration_status`. If `OPENAI_API_KEY` is missing:
|
|
14
|
+
|
|
15
|
+
- Tell the user the plugin needs an OpenAI API key from https://platform.openai.com/api-keys.
|
|
16
|
+
- If they installed the plugin via marketplace, they can re-run the setup or set the value in their plugin configuration (the manifest exposes `OPENAI_API_KEY` as a `userConfig` field).
|
|
17
|
+
- Note: OpenAI gates the gpt-image family behind organization verification — complete it at https://platform.openai.com/settings/organization/general if you get a 403.
|
|
18
|
+
|
|
19
|
+
## Prompting Best Practices
|
|
20
|
+
|
|
21
|
+
**Write narrative paragraphs, not comma-separated keyword lists.** gpt-image-2 is autoregressive and follows narrative instructions well.
|
|
22
|
+
|
|
23
|
+
1. **Start with image type**: "Create an educational diagram showing...", "Generate a photorealistic photograph of...", "Design a flat-style icon depicting..."
|
|
24
|
+
2. **Text in images works very well** — gpt-image-2 has ~99% text accuracy including CJK. Quote text exactly and describe font/placement.
|
|
25
|
+
3. **Specify layout explicitly**: side-by-side, top-to-bottom steps, centered with border, etc.
|
|
26
|
+
4. **Skip quality boosters** like "4k masterpiece", "highly detailed", "award-winning" — they add noise, not quality.
|
|
27
|
+
5. **Be specific about what you want**, not what you don't want. Positive descriptions work better than negations.
|
|
28
|
+
|
|
29
|
+
## Parameters
|
|
30
|
+
|
|
31
|
+
- **size** — `"1024x1024"` (default, square), `"1536x1024"` (landscape), `"1024x1536"` (portrait), `"2048x2048"` (square hi-res), `"auto"`, or a custom `"WxH"` (edges multiples of 16, max edge 3840, total pixels 655,360–8,294,400, ratio ≤ 3:1).
|
|
32
|
+
- **quality** — `"low"`, `"medium"`, `"high"`, `"auto"` (default). Higher quality increases latency and cost.
|
|
33
|
+
- **outputFormat** — `"png"` (default, lossless), `"jpeg"` (smaller, faster), `"webp"`.
|
|
34
|
+
- **background** — `"auto"` (default) or `"opaque"`. Transparent backgrounds are not supported by gpt-image-2.
|
|
35
|
+
- **numberOfImages** — `1` (default). Use 2–4 to explore variations.
|
|
36
|
+
- **returnInlineImage** — Consider setting to `false` in Claude Code to avoid context window overflow. The image is still saved to disk and can be viewed by the user.
|
|
37
|
+
|
|
38
|
+
## Workflow
|
|
39
|
+
|
|
40
|
+
1. **Generate**: Use `generate_image` with a well-crafted narrative prompt.
|
|
41
|
+
2. **Review**: Check the saved file path in the response.
|
|
42
|
+
3. **Refine**: Use `continue_editing` to make adjustments. Be specific about what to change.
|
|
43
|
+
4. **Iterate**: Each `continue_editing` call builds on the previous result.
|
|
44
|
+
|
|
45
|
+
## Editing with References
|
|
46
|
+
|
|
47
|
+
`edit_image` and `continue_editing` accept a main image plus optional `referenceImages` (up to 16 total). gpt-image-2 will fuse them per the prompt — useful for product composites, character consistency, or applying a style from one image to another. An optional `mask` (PNG with transparent pixels marking the editable area) can confine changes to a region.
|
|
48
|
+
|
|
49
|
+
## Style Guidance
|
|
50
|
+
|
|
51
|
+
- **Diagrams**: Specify colors, label positions, arrow directions. Use `quality: "high"` for complex layouts.
|
|
52
|
+
- **Illustrations**: Describe art style (flat, watercolor, line art), mood, and lighting.
|
|
53
|
+
- **Infographics**: Long numbered lists (>8 items) can still be unreliable; consider splitting.
|
|
54
|
+
- **Photos**: Describe camera angle, lighting conditions, depth of field, and subject positioning.
|
|
55
|
+
|
|
56
|
+
## Context Window Management
|
|
57
|
+
|
|
58
|
+
Base64 image data inlined in tool responses can fill the context window quickly (a 1024x1024 PNG is ~800 KB). Set `returnInlineImage: false` (or set the plugin's `MCP_GPT_IMAGE_2_INLINE_IMAGE` user config to `"false"`) to skip embedding it. The image is still saved to disk — tell the user the file path.
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: powerpoint-images
|
|
3
|
+
description: Use when creating images for PowerPoint, Keynote, or Google Slides presentations — hero slides, section dividers, accent illustrations, diagrams, or icon sets. Triggers on mentions of slides, decks, presentations, PowerPoint, Keynote, Google Slides, pitch deck, board deck, all-hands, QBR, town hall, sales deck, or "make me an image for slide X". A natural fit for Claude Cowork (knowledge workers building decks).
|
|
4
|
+
allowed-tools: mcp__gpt-image-2__generate_image, mcp__gpt-image-2__edit_image, mcp__gpt-image-2__continue_editing, mcp__gpt-image-2__get_configuration_status, mcp__gpt-image-2__get_last_image_info
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Slide Images with OpenAI gpt-image-2
|
|
8
|
+
|
|
9
|
+
Use this skill to generate imagery that sits well **inside a slide**, not as a standalone artwork. The constraint is always the same: the image must coexist with title text, body copy, and brand context — it cannot fight them.
|
|
10
|
+
|
|
11
|
+
## Prerequisite
|
|
12
|
+
|
|
13
|
+
Call `get_configuration_status` once per session if you aren't sure `OPENAI_API_KEY` is set. If missing, ask the user to add it to the MCP server's `env` block.
|
|
14
|
+
|
|
15
|
+
## Pick a size that matches the slide aspect
|
|
16
|
+
|
|
17
|
+
PowerPoint / Keynote / Google Slides defaults:
|
|
18
|
+
|
|
19
|
+
| Slide aspect | Use case | `size` |
|
|
20
|
+
|---|---|---|
|
|
21
|
+
| **16:9** (default modern decks) | Full-bleed hero, background, section divider | `1536x1024` (≈3:2, crops cleanly to 16:9) or custom `1920x1088` |
|
|
22
|
+
| **4:3** (legacy / corporate) | Full-bleed hero | `1024x1024` cropped, or custom `1440x1088` |
|
|
23
|
+
| Any | Centered accent illustration, mascot, spot art | `1024x1024` |
|
|
24
|
+
| Any | Vertical pull-quote panel, side rail | `1024x1536` |
|
|
25
|
+
|
|
26
|
+
Custom sizes must follow gpt-image-2 constraints (multiples of 16, max edge 3840, ratio ≤ 3:1). The server validates these.
|
|
27
|
+
|
|
28
|
+
Quick rule: **if it fills the slide → landscape; if it sits beside text → square; if it's a side rail → portrait.**
|
|
29
|
+
|
|
30
|
+
## Style guidance for slides
|
|
31
|
+
|
|
32
|
+
Slide imagery succeeds when it **supports the message at a glance from 3 meters away**. Apply these:
|
|
33
|
+
|
|
34
|
+
1. **High contrast, low complexity.** Backgrounds should have one focal point and breathing room for overlaid text. Avoid dense textures in the upper-left or center where titles usually land.
|
|
35
|
+
2. **One dominant color or palette.** Tell gpt-image-2 the exact palette ("muted navy, warm cream, single coral accent") so it doesn't clash with the deck's theme.
|
|
36
|
+
3. **Negative space on purpose.** For hero images, explicitly ask for negative space on a specific side: *"…with the left third left empty for a title overlay."*
|
|
37
|
+
4. **Flat / editorial styles over photorealism** for most business decks. Photorealism is great for product shots, hero customer stories, or industry imagery — not for conceptual slides.
|
|
38
|
+
5. **Consistency across the deck.** When generating multiple slides, reuse the **same style sentence** verbatim in every prompt ("flat illustration, single warm-coral accent color on cream background, soft geometric shapes"). Then vary only the subject. This is the single biggest lever for a coherent deck.
|
|
39
|
+
6. **No fake logos, no fake brand names.** They'll look amateur and trigger legal review. Describe generic objects instead ("a laptop showing a generic dashboard with bar charts").
|
|
40
|
+
|
|
41
|
+
## Text inside the image — use sparingly
|
|
42
|
+
|
|
43
|
+
gpt-image-2's text accuracy is high (~99%), but **text on the image competes with text on the slide**. Use it only when intentional:
|
|
44
|
+
|
|
45
|
+
- ✅ A single short label or callout (≤ 25 chars) integrated into the illustration
|
|
46
|
+
- ✅ A stylized number for a section header ("01", "02")
|
|
47
|
+
- ❌ Bullet points (put those in the slide, not the image)
|
|
48
|
+
- ❌ Paragraphs (the slide already has text)
|
|
49
|
+
|
|
50
|
+
When you do request text, quote it exactly and specify the font style: *"the word 'Pipeline' in bold sans-serif, integrated into the illustration as a banner across the top."*
|
|
51
|
+
|
|
52
|
+
## Workflow
|
|
53
|
+
|
|
54
|
+
1. **Confirm the slide context first.** Aspect, palette, what text will overlay it, and whether it's hero / accent / icon. If the user didn't say, ask in one line or pick a sensible default and call it out.
|
|
55
|
+
2. **Build a reusable style sentence** (color palette + art style + medium). Save it in your head for the whole deck.
|
|
56
|
+
3. **Generate** with `generate_image`. Default to `quality: "high"` for hero slides (worth the latency), `quality: "medium"` for accent illustrations.
|
|
57
|
+
4. **Refine** with `continue_editing` — typical asks: "move the subject right to leave the left third empty", "warmer color temperature", "remove the small text in the corner".
|
|
58
|
+
5. **Batch related slides.** When you need 4–8 spot illustrations for the same deck, call `generate_image` once per concept with `numberOfImages: 2` to pick the best of each pair, reusing the exact same style sentence each time.
|
|
59
|
+
|
|
60
|
+
## Recommended defaults for slides
|
|
61
|
+
|
|
62
|
+
```
|
|
63
|
+
size: "1536x1024" // 16:9 hero
|
|
64
|
+
quality: "high" // worth it for slide imagery
|
|
65
|
+
outputFormat: "png" // lossless; designers will recompress
|
|
66
|
+
background: "opaque" // gpt-image-2 doesn't do transparency anyway
|
|
67
|
+
returnInlineImage: false // file path is enough; saves context
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
For spot illustrations / icons that will be placed inside a layout, switch `size` to `"1024x1024"` and `quality` to `"medium"`.
|
|
71
|
+
|
|
72
|
+
## Cost note
|
|
73
|
+
|
|
74
|
+
`quality: "high"` at large sizes is the expensive setting. For a 30-slide deck, generate **hero slides** at high quality and **accent illustrations** at medium. The visual difference between medium and high on a 1024x1024 spot illustration on a slide is rarely worth the price multiplier.
|
|
75
|
+
|
|
76
|
+
## Context Window Management
|
|
77
|
+
|
|
78
|
+
Always set `returnInlineImage: false` when generating multiple deck images in one session — base64 of a 1536x1024 PNG can be ~1.5 MB and will fill the context fast. The file path is enough for the user to drop the image into their slide tool. (In Cowork, you can also tell the user the saved path and let them drag the file into the PowerPoint/Keynote/Slides window.)
|