mcp-duojie-image 0.7.0 → 0.8.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/README.md +5 -1
- package/dist/index.js +4 -1
- package/dist/prompt.js +1 -0
- package/dist/provider.js +23 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -5,7 +5,9 @@ MCP server for image generation via duojie.games GPT image API.
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
7
|
- Text-to-image generation with `gpt-image-2`
|
|
8
|
-
- Reference image
|
|
8
|
+
- Reference image support with role-based endpoint routing:
|
|
9
|
+
- `reference` / `style_reference` / `composition_reference` / `insert_source` → `/v1/images/generations` (JSON body + base64 data URIs; stable endpoint)
|
|
10
|
+
- `edit_target` → `/v1/images/edits` (multipart form; for modifying an existing image)
|
|
9
11
|
- Prompt structuring helpers for use case, asset type, constraints, and effective prompt echo
|
|
10
12
|
- Optional chroma-key transparent background post-processing
|
|
11
13
|
- Async submission with task status lookup
|
|
@@ -61,6 +63,8 @@ Supported `input_images.role` values:
|
|
|
61
63
|
- `composition_reference`
|
|
62
64
|
- `insert_source`
|
|
63
65
|
|
|
66
|
+
**Endpoint routing:** Images with the `edit_target` role are sent to `/v1/images/edits` (multipart form). All other roles (`reference`, `style_reference`, `composition_reference`, `insert_source`) are sent to `/v1/images/generations` as base64 data URIs in a JSON body. This mirrors Codex's imagegen skill, which treats style/composition/mood references as "generate" rather than "edit". If a request mixes `edit_target` with other roles, the edits endpoint is used.
|
|
67
|
+
|
|
64
68
|
When both `input_images` and `reference_images` are provided, `input_images` takes precedence. `generate_image` returns `effective_prompt` so callers can diagnose the exact prompt sent upstream.
|
|
65
69
|
|
|
66
70
|
Response:
|
package/dist/index.js
CHANGED
|
@@ -34,7 +34,7 @@ const server = new McpServer({
|
|
|
34
34
|
version: "0.1.0",
|
|
35
35
|
});
|
|
36
36
|
server.registerTool("generate_image", {
|
|
37
|
-
description: "Generate image async with gpt-image-2. Return after tool call; don't wait/check.",
|
|
37
|
+
description: "Generate image async with gpt-image-2. Reference/style/composition images route through /v1/images/generations (stable); edit_target role routes through /v1/images/edits. Return after tool call; don't wait/check.",
|
|
38
38
|
inputSchema: z.object({
|
|
39
39
|
prompt: z.string().min(1).describe("Prompt"),
|
|
40
40
|
output_dir: z.string().describe("Absolute output dir"),
|
|
@@ -105,6 +105,8 @@ server.registerTool("generate_image", {
|
|
|
105
105
|
}
|
|
106
106
|
const task = createTask();
|
|
107
107
|
updateTask(task.id, { effectivePrompt: preparedPrompt.prompt });
|
|
108
|
+
// 只有 edit_target 角色才走 edits 端点;reference/style/composition 走 generations
|
|
109
|
+
const hasEditTarget = preparedPrompt.imageInputs?.some((img) => img.role === "edit_target") ?? false;
|
|
108
110
|
// Start background generation (don't await)
|
|
109
111
|
(async () => {
|
|
110
112
|
try {
|
|
@@ -112,6 +114,7 @@ server.registerTool("generate_image", {
|
|
|
112
114
|
let imageBuffer = await generateImage(config, {
|
|
113
115
|
prompt: preparedPrompt.prompt,
|
|
114
116
|
referenceImages: preparedPrompt.imagePaths,
|
|
117
|
+
useEditsEndpoint: hasEditTarget,
|
|
115
118
|
size: args.size ?? "1K",
|
|
116
119
|
aspectRatio: args.aspect_ratio,
|
|
117
120
|
referenceMaxSide: args.preserve_reference_detail ? false : args.reference_max_side,
|
package/dist/prompt.js
CHANGED
package/dist/provider.js
CHANGED
|
@@ -105,12 +105,16 @@ async function generateOpenAIImage(config, opts) {
|
|
|
105
105
|
const prompt = opts.prompt;
|
|
106
106
|
const size = mapOpenAISize(opts.size, opts.aspectRatio);
|
|
107
107
|
const hasReferenceImages = Boolean(opts.referenceImages?.length);
|
|
108
|
-
|
|
108
|
+
// edit_target → /v1/images/edits (multipart form)
|
|
109
|
+
// reference/style/composition → /v1/images/generations (JSON body + base64 data URIs)
|
|
110
|
+
const useEdits = hasReferenceImages && opts.useEditsEndpoint === true;
|
|
111
|
+
const url = `${config.baseUrl}${useEdits ? "/v1/images/edits" : "/v1/images/generations"}`;
|
|
109
112
|
let body;
|
|
110
113
|
let headers = {
|
|
111
114
|
Authorization: `Bearer ${config.apiKey}`,
|
|
112
115
|
};
|
|
113
|
-
if (
|
|
116
|
+
if (useEdits) {
|
|
117
|
+
// edit_target: multipart form(修改已有图片)
|
|
114
118
|
const form = new FormData();
|
|
115
119
|
form.set("model", GPT_IMAGE_MODEL);
|
|
116
120
|
form.set("prompt", prompt);
|
|
@@ -125,7 +129,24 @@ async function generateOpenAIImage(config, opts) {
|
|
|
125
129
|
}
|
|
126
130
|
body = form;
|
|
127
131
|
}
|
|
132
|
+
else if (hasReferenceImages) {
|
|
133
|
+
// reference/style/composition: generations + base64 data URIs in JSON body
|
|
134
|
+
const imageUrls = [];
|
|
135
|
+
for (const imgPath of opts.referenceImages) {
|
|
136
|
+
const data = await resizeIfNeeded(imgPath, opts.referenceMaxSide);
|
|
137
|
+
imageUrls.push(`data:image/webp;base64,${data.toString("base64")}`);
|
|
138
|
+
}
|
|
139
|
+
headers = { ...headers, "Content-Type": "application/json" };
|
|
140
|
+
body = JSON.stringify({
|
|
141
|
+
model: GPT_IMAGE_MODEL,
|
|
142
|
+
prompt,
|
|
143
|
+
size,
|
|
144
|
+
image: imageUrls,
|
|
145
|
+
...(opts.aspectRatio ? { aspect_ratio: opts.aspectRatio } : {}),
|
|
146
|
+
});
|
|
147
|
+
}
|
|
128
148
|
else {
|
|
149
|
+
// 纯文本生成
|
|
129
150
|
headers = { ...headers, "Content-Type": "application/json" };
|
|
130
151
|
body = JSON.stringify({
|
|
131
152
|
model: GPT_IMAGE_MODEL,
|