@coconutmilk/qwen-image-edit-mcp-server 1.0.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.
Files changed (2) hide show
  1. package/index.js +157 -0
  2. package/package.json +12 -0
package/index.js ADDED
@@ -0,0 +1,157 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
4
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
5
+ import { z } from "zod";
6
+
7
+ // ========================================
8
+ // 1. 创建MCP Server
9
+ // ========================================
10
+ const server = new McpServer({
11
+ name: "qwen-image-edit",
12
+ version: "1.0.0"
13
+ });
14
+
15
+ // ========================================
16
+ // 2. 提交异步任务
17
+ // ========================================
18
+ async function submitTask(images, text, options) {
19
+ const content = [];
20
+ for (const imageUrl of images) {
21
+ content.push({ image: imageUrl });
22
+ }
23
+ content.push({ text: text });
24
+
25
+ const response = await fetch(
26
+ "https://dashscope.aliyuncs.com/api/v1/services/aigc/multimodal-generation/generation",
27
+ {
28
+ method: "POST",
29
+ headers: {
30
+ "Content-Type": "application/json",
31
+ "Authorization": `Bearer ${process.env.DASHSCOPE_API_KEY}`,
32
+ "X-DashScope-Async": "enable"
33
+ },
34
+ body: JSON.stringify({
35
+ model: "qwen-image-edit-plus-2025-12-15",
36
+ input: {
37
+ messages: [{ role: "user", content: content }]
38
+ },
39
+ parameters: {
40
+ n: options.n || 1,
41
+ size: options.size || "1024*1024",
42
+ prompt_extend: options.prompt_extend !== false,
43
+ watermark: options.watermark || false,
44
+ negative_prompt: options.negative_prompt || " "
45
+ }
46
+ })
47
+ }
48
+ );
49
+
50
+ const result = await response.json();
51
+ if (result.code) {
52
+ throw new Error(`提交任务失败: ${result.code} - ${result.message}`);
53
+ }
54
+ return result.output.task_id;
55
+ }
56
+
57
+ // ========================================
58
+ // 3. 轮询任务结果
59
+ // ========================================
60
+ async function pollResult(taskId) {
61
+ const maxRetries = 60;
62
+ const interval = 3000;
63
+
64
+ for (let i = 0; i < maxRetries; i++) {
65
+ const response = await fetch(
66
+ `https://dashscope.aliyuncs.com/api/v1/tasks/${taskId}`,
67
+ {
68
+ method: "GET",
69
+ headers: {
70
+ "Authorization": `Bearer ${process.env.DASHSCOPE_API_KEY}`
71
+ }
72
+ }
73
+ );
74
+
75
+ const result = await response.json();
76
+ const status = result.output.task_status;
77
+
78
+ if (status === "SUCCEEDED") {
79
+ return result.output.results;
80
+ } else if (status === "FAILED") {
81
+ throw new Error(`任务失败: ${result.output.message || "未知错误"}`);
82
+ }
83
+
84
+ await new Promise(resolve => setTimeout(resolve, interval));
85
+ }
86
+
87
+ throw new Error("任务超时,超过3分钟未完成");
88
+ }
89
+
90
+ // ========================================
91
+ // 4. 注册工具(新版 registerTool 写法)
92
+ // ========================================
93
+ server.registerTool(
94
+ // 第一个参数:工具名
95
+ "edit_image",
96
+ // 第二个参数:config对象
97
+ {
98
+ title: "图片编辑工具",
99
+ description: "使用千问图像编辑模型编辑图片。支持单图编辑和多图融合,可修改文字、增删物体、改变动作、迁移风格等。",
100
+ inputSchema: {
101
+ images: z.array(z.string()).min(1).max(6)
102
+ .describe("图片URL数组,1-6张图片"),
103
+ text: z.string()
104
+ .describe("编辑指令,描述你想怎么编辑图片,比如'把背景换成海滩'"),
105
+ n: z.number().min(1).max(6).optional()
106
+ .describe("生成图片数量,默认1"),
107
+ size: z.string().optional()
108
+ .describe("输出图片尺寸,如'1024*1024'、'1024*1536',默认1024*1024"),
109
+ negative_prompt: z.string().optional()
110
+ .describe("负向提示词,描述不想出现的内容"),
111
+ prompt_extend: z.boolean().optional()
112
+ .describe("是否启用提示词智能改写,默认true"),
113
+ watermark: z.boolean().optional()
114
+ .describe("是否添加水印,默认false")
115
+ },
116
+ annotations: {
117
+ title: "图片编辑工具",
118
+ readOnlyHint: false,
119
+ destructiveHint: false,
120
+ openWorldHint: true
121
+ }
122
+ },
123
+ // 第三个参数:handler函数
124
+ async ({ images, text, n, size, negative_prompt, prompt_extend, watermark }) => {
125
+ try {
126
+ const taskId = await submitTask(images, text, {
127
+ n, size, negative_prompt, prompt_extend, watermark
128
+ });
129
+ const results = await pollResult(taskId);
130
+
131
+ const content = [];
132
+ content.push({
133
+ type: "text",
134
+ text: `图片编辑完成,共生成 ${results.length} 张图片:`
135
+ });
136
+ for (let i = 0; i < results.length; i++) {
137
+ content.push({
138
+ type: "text",
139
+ text: `图片${i + 1}: ${results[i].url}`
140
+ });
141
+ }
142
+ return { content };
143
+
144
+ } catch (error) {
145
+ return {
146
+ content: [{ type: "text", text: `编辑失败: ${error.message}` }],
147
+ isError: true
148
+ };
149
+ }
150
+ }
151
+ );
152
+
153
+ // ========================================
154
+ // 5. 启动
155
+ // ========================================
156
+ const transport = new StdioServerTransport();
157
+ await server.connect(transport);
package/package.json ADDED
@@ -0,0 +1,12 @@
1
+ {
2
+ "name": "@coconutmilk/qwen-image-edit-mcp-server",
3
+ "version": "1.0.0",
4
+ "type": "module",
5
+ "bin": {
6
+ "qwen-image-edit-mcp-server": "./index.js"
7
+ },
8
+ "dependencies": {
9
+ "@modelcontextprotocol/sdk": "^1.0.0",
10
+ "zod": "^3.23.0"
11
+ }
12
+ }