From 0516444515816a04d07d9a758ea867125031ff55 Mon Sep 17 00:00:00 2001 From: Ace Data Cloud Dev Date: Sun, 11 Oct 2026 23:52:15 +0800 Subject: [PATCH 1/4] feat(third_party): add Qwen Image plugin --- .cursor-plugin/marketplace.json | 5 + README.md | 1 + .../qwen-image/.cursor-plugin/plugin.json | 32 +++ third_party/qwen-image/CHANGELOG.md | 5 + third_party/qwen-image/LICENSE | 21 ++ third_party/qwen-image/README.md | 47 +++++ third_party/qwen-image/assets/logo.png | Bin 0 -> 6701 bytes .../qwen-image/examples/first-image.md | 5 + third_party/qwen-image/mcp.json | 13 ++ .../qwen-image/readme/README_zh_Hans.md | 47 +++++ third_party/qwen-image/server.mjs | 189 ++++++++++++++++++ .../qwen-image/skills/qwen-image/SKILL.md | 15 ++ .../qwen-image/tests/protocol.test.mjs | 47 +++++ 13 files changed, 427 insertions(+) create mode 100644 third_party/qwen-image/.cursor-plugin/plugin.json create mode 100644 third_party/qwen-image/CHANGELOG.md create mode 100644 third_party/qwen-image/LICENSE create mode 100644 third_party/qwen-image/README.md create mode 100644 third_party/qwen-image/assets/logo.png create mode 100644 third_party/qwen-image/examples/first-image.md create mode 100644 third_party/qwen-image/mcp.json create mode 100644 third_party/qwen-image/readme/README_zh_Hans.md create mode 100644 third_party/qwen-image/server.mjs create mode 100644 third_party/qwen-image/skills/qwen-image/SKILL.md create mode 100644 third_party/qwen-image/tests/protocol.test.mjs diff --git a/.cursor-plugin/marketplace.json b/.cursor-plugin/marketplace.json index f0c3bd371..0b7fe0c3c 100644 --- a/.cursor-plugin/marketplace.json +++ b/.cursor-plugin/marketplace.json @@ -543,6 +543,11 @@ "source": "third_party/square", "description": "Work with payments, orders, catalog, customers, and invoices." }, + { + "name": "qwen-image", + "source": "third_party/qwen-image", + "description": "Generate or edit Qwen Image pictures through Ace Data Cloud and retrieve completed results." + }, { "name": "tailscale", "source": "third_party/tailscale", diff --git a/README.md b/README.md index 886849908..de819e411 100644 --- a/README.md +++ b/README.md @@ -100,6 +100,7 @@ Official Cursor plugins for popular developer tools, frameworks, and SaaS produc | `zoho-desk` | [Zoho Desk](third_party/zoho-desk/) | Cursor | Integrations | Work with tickets, contacts, tasks, and knowledge base articles in Zoho Desk. | | `zoho-books` | [Zoho Books](third_party/zoho-books/) | Cursor | Integrations | Work with contacts, invoices, estimates, and items in Zoho Books. | | `square` | [Square](third_party/square/) | Cursor | Integrations | Work with payments, orders, catalog, customers, and invoices. | +| `qwen-image` | [Qwen Image](third_party/qwen-image/) | Ace Data Cloud Dev | Integrations | Generate or edit Qwen Image pictures through Ace Data Cloud and retrieve completed results. | | `tailscale` | [Tailscale](third_party/tailscale/) | Cursor | Integrations | Let Grok Bot reach MCP servers on your private Tailscale network. On your own tailnet it joins as one tag:grokbot device; on your team's company tailnet, turned on by a team admin, it uses each member's own device. | | `highlevel` | [HighLevel](third_party/highlevel/) | Cursor | Integrations | Work with contacts, conversations, opportunities, calendars, and payments. | | `finary` | [Finary](third_party/finary/) | Cursor | Integrations | Read your net worth, accounts, holdings, spending, and budgets. | diff --git a/third_party/qwen-image/.cursor-plugin/plugin.json b/third_party/qwen-image/.cursor-plugin/plugin.json new file mode 100644 index 000000000..5f4ad7888 --- /dev/null +++ b/third_party/qwen-image/.cursor-plugin/plugin.json @@ -0,0 +1,32 @@ +{ + "name": "qwen-image", + "displayName": "Qwen Image", + "version": "1.0.0", + "minClientVersions": { + "cursor": "3.13.0" + }, + "description": "Generate or edit Qwen Image pictures through Ace Data Cloud and retrieve completed results.", + "author": { + "name": "Ace Data Cloud Dev", + "email": "dev@acedata.cloud" + }, + "homepage": "https://platform.acedata.cloud/models", + "repository": "https://github.com/AceDataCloud/QwenImageCursor", + "license": "MIT", + "logo": "assets/logo.png", + "keywords": ["qwen image", "qwen", "image generation", "acedatacloud", "mcp"], + "category": "integrations", + "tags": ["qwen", "image", "mcp"], + "variables": { + "type": "object", + "properties": { + "ACEDATACLOUD_API_TOKEN": { + "type": "string", + "title": "Ace Data Cloud API token", + "description": "Service API key from https://platform.acedata.cloud/console/applications with Qwen Image access." + } + }, + "required": ["ACEDATACLOUD_API_TOKEN"] + }, + "mcpServers": "./mcp.json" +} diff --git a/third_party/qwen-image/CHANGELOG.md b/third_party/qwen-image/CHANGELOG.md new file mode 100644 index 000000000..78b59af41 --- /dev/null +++ b/third_party/qwen-image/CHANGELOG.md @@ -0,0 +1,5 @@ +# Changelog + +## 1.0.0 + +- Initial Qwen Image Cursor plugin with a small local MCP server for generation and same-task retrieval. diff --git a/third_party/qwen-image/LICENSE b/third_party/qwen-image/LICENSE new file mode 100644 index 000000000..64bb5cfce --- /dev/null +++ b/third_party/qwen-image/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Ace Data Cloud + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/third_party/qwen-image/README.md b/third_party/qwen-image/README.md new file mode 100644 index 000000000..1d8b46d98 --- /dev/null +++ b/third_party/qwen-image/README.md @@ -0,0 +1,47 @@ +# Qwen Image for Cursor + +Generate and edit Qwen Image pictures through [Ace Data Cloud](https://platform.acedata.cloud) in Cursor. This focused plugin ships a dependency-free local MCP server with generation and same-task retrieval tools. It uses the current Qwen Image API contract already verified by the Ace Data Cloud Dify integration. + +[简体中文教程](readme/README_zh_Hans.md) · [Copyable first-run prompt](examples/first-image.md) · [Current pricing](https://platform.acedata.cloud/models) + +## 1. Install + +Install [Node.js 20 or later](https://nodejs.org/en/download) and check `node --version` in a terminal. The plugin starts its local MCP server with `node`; it does not download packages or run a remote executable. + +In Cursor 3.24, open **Customize → Browse Marketplace**, search for **Qwen Image**, confirm the **Ace Data Cloud Dev** author and Qwen Image logo, then click **Add**. Earlier Cursor versions may put Plugins under Settings. If the official listing is not live, copy this directory to `~/.cursor/plugins/local/qwen-image/`, then open **Customize → Plugins** and confirm the **Local** label. Local installation does not prove Marketplace publication. + +## 2. Get the right API key + +1. Sign in at [Ace Data Cloud Applications](https://platform.acedata.cloud/console/applications). +2. Open **General application** or a Qwen Image-specific application. Copy an existing service API key, or use **Manage Keys → Create** for a separate Cursor key. +3. Check Qwen Image access, [current pricing](https://platform.acedata.cloud/models), and balance. If **Allowed APIs** restrictions are enabled, include both `/qwen-image/images` and `/qwen-image/tasks`. + +Use a service API key, not a platform management token. Do not put it in prompts or repository files. + +## 3. Configure the plugin + +Open **Customize → Plugins → Qwen Image → Configure**, paste the key string into **Ace Data Cloud API token**, and save. Team administrators can configure the same variable in **Dashboard → Plugins → Configure**. The plugin passes the configured value to its local MCP server as `ACEDATACLOUD_API_TOKEN` and adds the Bearer header itself. + +The bundled `mcp.json` starts `server.mjs` from `CURSOR_PLUGIN_ROOT`. It uses Node's built-in fetch and no npm packages. If the MCP does not start, check that `node` is on Cursor's process PATH and reconnect the plugin. + +## 4. Make and retrieve the first image + +Paste the [first-image prompt](examples/first-image.md) into Cursor Agent. It requests one `qwen_image_generate` call with model `qwen-image-3.0`, size `1024*1024`, count `1`, and no watermark. Use a model available to your account; the current service contract is the source of truth. + +The paid generation returns a `task_id` while work may still be pending. Keep it. Call `qwen_image_get_task` with that **same** ID until `status=succeeded` and an image URL appears. If the chat is interrupted, begin a new chat with “Query Qwen Image task ; do not generate again.” Open the returned HTTPS PNG and check the image. + +In **Ace Data Cloud → Usage/Billing**, match the one generation to its deducted Credits record. Task queries do not submit another generation. The plugin is free; API calls follow current service pricing. + +## Troubleshooting + +| Symptom | Action | +| --- | --- | +| Plugin not listed | Check the exact Qwen Image entry and author. A local plugin is separate from the public Marketplace. | +| MCP server will not start | Run `node --version`, confirm Node 20+ is on Cursor's PATH, and reconnect the plugin. | +| 401 or 403 | Check the service key, expiration, Qwen Image access, balance, and both Allowed APIs. Paste only the key, without `Bearer `. | +| Invalid model or size | Use a currently supported model and `WIDTH*HEIGHT` size such as `1024*1024`. | +| Pending or interrupted task | Query the original task ID; do not regenerate to poll. | +| Timeout or 5xx | Check the task and usage history before deciding whether another paid request is needed. | +| Image URL fails | Check terminal success and use the URL returned by that task. | + +[Source](https://github.com/AceDataCloud/QwenImageCursor) · [Report a problem](https://github.com/AceDataCloud/QwenImageCursor/issues) · dev@acedata.cloud. The logo reuses the existing Ace Data Cloud Qwen Image integration asset. This is a tool plugin; it does not register a Cursor chat-model provider. diff --git a/third_party/qwen-image/assets/logo.png b/third_party/qwen-image/assets/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..8a17e05d7c52dd3ceb5ec43fc7c7be1bc8fa30ef GIT binary patch literal 6701 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 k3sD9Q3*rCowS&WJUvWwq<%f>`djkP Use the Qwen Image plugin to make one 1024×1024 image of a single blue paper sphere on a cream background, as a clean studio product photograph with no text. Use `qwen-image-3.0`, `n: 1`, and `watermark: false` if available to this account. Call `qwen_image_generate` only once, save its task ID, then use `qwen_image_get_task` with the same ID until it succeeds or fails. Open the completed PNG URL and tell me the original task ID. If it is still pending, give me that ID so I can resume without another generation. diff --git a/third_party/qwen-image/mcp.json b/third_party/qwen-image/mcp.json new file mode 100644 index 000000000..dcbddef17 --- /dev/null +++ b/third_party/qwen-image/mcp.json @@ -0,0 +1,13 @@ +{ + "mcpServers": { + "qwen-image": { + "type": "stdio", + "command": "node", + "args": ["${CURSOR_PLUGIN_ROOT}/server.mjs"], + "cwd": "${CURSOR_PLUGIN_ROOT}", + "env": { + "ACEDATACLOUD_API_TOKEN": "${ACEDATACLOUD_API_TOKEN}" + } + } + } +} diff --git a/third_party/qwen-image/readme/README_zh_Hans.md b/third_party/qwen-image/readme/README_zh_Hans.md new file mode 100644 index 000000000..d8964a6fa --- /dev/null +++ b/third_party/qwen-image/readme/README_zh_Hans.md @@ -0,0 +1,47 @@ +# Cursor Qwen Image 插件 + +通过 [Ace Data Cloud](https://platform.acedata.cloud) 在 Cursor 中生成或编辑 Qwen Image 图片。插件自带无第三方依赖的本地 MCP 服务,提供生成和同 task ID 查询工具;请求参数沿用 Ace Data Cloud Dify 集成已验证的当前服务契约。 + +[English guide](../README.md) · [可复制的首跑提示词](../examples/first-image.md) · [当前价格](https://platform.acedata.cloud/models) + +## 1. 安装 + +先安装 [Node.js 20 或更新版本](https://nodejs.org/en/download),并在终端运行 `node --version`。插件用 `node` 启动本地 MCP,不下载 npm 包或远端可执行程序。 + +在 Cursor 3.24 打开 **Customize → Browse Marketplace**,搜索 **Qwen Image**,确认作者 **Ace Data Cloud Dev** 和服务图标,再点击 **Add**。较早版本可能将 Plugins 放在 Settings。官方条目尚未出现时,可把本目录复制到 `~/.cursor/plugins/local/qwen-image/`,再到 **Customize → Plugins** 查看 **Local** 标签;本地安装不代表 Marketplace 已上架。 + +## 2. 获取服务 API Key + +1. 登录 [Ace Data Cloud 应用管理](https://platform.acedata.cloud/console/applications)。 +2. 打开 **General application** 或 Qwen Image 专用应用,复制已有服务 Key;也可通过 **Manage Keys → Create** 新建 Cursor 专用 Key。 +3. 确认 Qwen Image 权限、[当前价格](https://platform.acedata.cloud/models)和余额。若启用了 **Allowed APIs**,同时允许 `/qwen-image/images` 和 `/qwen-image/tasks`。 + +使用服务 API Key,不要使用平台管理 token;也不要把 Key 写进提示词或仓库。 + +## 3. 配置插件 + +打开 **Customize → Plugins → Qwen Image → Configure**,将 Key 原文填入 **Ace Data Cloud API token** 并保存。团队管理员也可通过 **Dashboard → Plugins → Configure** 配置同一变量。插件把值作为 `ACEDATACLOUD_API_TOKEN` 传给本地 MCP,并自行加上 Bearer 请求头。 + +`mcp.json` 从 `CURSOR_PLUGIN_ROOT` 启动 `server.mjs`,只使用 Node 内置 fetch。若 MCP 没有启动,检查 Cursor 进程 PATH 中能找到 `node`,然后重连插件。 + +## 4. 生成并取回首张图片 + +在 Cursor Agent 中复制[首跑提示词](../examples/first-image.md)。示例只调用一次 `qwen_image_generate`,使用账户可用时的 `qwen-image-3.0`、尺寸 `1024*1024`、数量 `1`、无水印。实际可用模型以当前服务契约为准。 + +付费生成可能先返回 `task_id`,此时图片仍在处理。保存这个 ID,沿同一 ID 调用 `qwen_image_get_task`,直到 `status=succeeded` 并返回图片地址。聊天中断时新开聊天说“查询 Qwen Image 任务 ,不要重新生成”。打开最终 HTTPS PNG 检查图片。 + +到 **Ace Data Cloud → Usage/Billing** 核对这一次生成对应的 Credits 扣费。任务查询不会再提交生成。插件免费,API 调用按当前服务价格计费。 + +## 排障 + +| 现象 | 处理 | +| --- | --- | +| 找不到插件 | 核对 Qwen Image 条目和作者;本地插件与官方市场是两条渠道。 | +| MCP 未启动 | 运行 `node --version`,确认 Node 20+ 在 Cursor 的 PATH 中,再重连。 | +| 401 或 403 | 检查服务 Key、有效期、权限、余额和两个 Allowed APIs;配置值不要带 `Bearer `。 | +| 模型或尺寸错误 | 用当前支持的模型,尺寸写成 `WIDTH*HEIGHT`,例如 `1024*1024`。 | +| pending 或任务中断 | 沿原 task ID 查询,不要为轮询再次生成。 | +| 超时或 5xx | 先检查原任务和用量记录,再决定是否需要另一笔付费请求。 | +| 图片无法打开 | 确认终态成功,使用该任务最终返回的地址。 | + +[源码](https://github.com/AceDataCloud/QwenImageCursor) · [问题反馈](https://github.com/AceDataCloud/QwenImageCursor/issues) · dev@acedata.cloud。图标复用 Ace Data Cloud 现有 Qwen Image 集成资源。本插件提供工具,不注册 Cursor 聊天模型 Provider。 diff --git a/third_party/qwen-image/server.mjs b/third_party/qwen-image/server.mjs new file mode 100644 index 000000000..e9a3793c1 --- /dev/null +++ b/third_party/qwen-image/server.mjs @@ -0,0 +1,189 @@ +import { createInterface } from "node:readline"; +import { stdin, stdout } from "node:process"; +import { pathToFileURL } from "node:url"; + +const API_ORIGIN = "https://api.acedata.cloud"; +const MODELS = ["qwen-image-3.0", "qwen-image-3.0-pro"]; +const TASK_PATH = "/qwen-image/tasks"; + +export const tools = [ + { + name: "qwen_image_generate", + description: "Submit one Qwen Image generation or edit through Ace Data Cloud. This is a paid operation. Save the returned task_id and query that same task; do not call generate again to poll.", + inputSchema: { + type: "object", + properties: { + prompt: { type: "string", minLength: 1, maxLength: 18000, description: "Describe the desired image or edit." }, + model: { type: "string", enum: MODELS, default: "qwen-image-3.0" }, + size: { type: "string", pattern: "^[0-9]+\\*[0-9]+$", default: "1024*1024" }, + n: { type: "integer", minimum: 1, maximum: 6, default: 1 }, + watermark: { type: "boolean", default: false }, + image_urls: { + type: "array", + minItems: 1, + maxItems: 3, + items: { type: "string", format: "uri" }, + description: "Optional reference image URLs for editing." + } + }, + required: ["prompt"] + }, + annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: true } + }, + { + name: "qwen_image_get_task", + description: "Query an existing Qwen Image task by the original task_id. This does not submit another generation.", + inputSchema: { + type: "object", + properties: { + task_id: { type: "string", minLength: 1, description: "The ID returned by qwen_image_generate." } + }, + required: ["task_id"] + }, + annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: true } + } +]; + +function textResult(value, isError = false) { + return { content: [{ type: "text", text: JSON.stringify(value) }], isError }; +} + +function requireToken() { + const token = process.env.ACEDATACLOUD_API_TOKEN; + if (!token) throw new Error("Configure ACEDATACLOUD_API_TOKEN in the Cursor plugin."); + return token; +} + +async function post(path, payload, timeoutMs) { + const token = requireToken(); + let response; + try { + response = await fetch(API_ORIGIN + path, { + method: "POST", + headers: { + Authorization: "Bearer " + token, + "Content-Type": "application/json" + }, + body: JSON.stringify(payload), + signal: AbortSignal.timeout(timeoutMs) + }); + } catch { + throw new Error("Network request failed or timed out. Check task and usage history before any new paid submission."); + } + let data; + try { + data = await response.json(); + } catch { + throw new Error("Ace Data Cloud returned a non-JSON response (HTTP " + response.status + ")."); + } + if (!response.ok) { + const traceId = typeof data?.trace_id === "string" ? data.trace_id : ""; + throw new Error("Ace Data Cloud request failed (HTTP " + response.status + ")" + (traceId ? "; trace_id " + traceId : "") + "."); + } + return data; +} + +export function normalizeTask(body, requestedId = "") { + const taskId = String(body?.id || body?.task_id || body?.response?.task_id || requestedId); + const response = body?.response && typeof body.response === "object" ? body.response : {}; + const data = Array.isArray(response.data) ? response.data : []; + const mediaUrls = data + .map((item) => item && (item.image_url || item.url)) + .filter((url) => typeof url === "string" && /^https:\/\//.test(url)); + const unfinished = body?.finished_at === null; + const succeeded = !unfinished && response.success === true; + const failed = !unfinished && response.success === false; + return { + status: succeeded ? "succeeded" : failed ? "failed" : "pending", + success: succeeded, + task_id: taskId, + trace_id: typeof body?.trace_id === "string" ? body.trace_id : "", + media_urls: succeeded ? mediaUrls : [] + }; +} + +function generationPayload(args) { + if (!args || typeof args.prompt !== "string" || !args.prompt.trim() || args.prompt.length > 18000) { + throw new Error("prompt must contain 1 to 18000 characters."); + } + const model = args.model ?? "qwen-image-3.0"; + if (!MODELS.includes(model)) throw new Error("Unsupported Qwen Image model."); + const size = args.size ?? "1024*1024"; + if (typeof size !== "string" || !/^[0-9]+\*[0-9]+$/.test(size)) { + throw new Error("size must be WIDTH*HEIGHT, for example 1024*1024."); + } + const n = args.n ?? 1; + if (!Number.isInteger(n) || n < 1 || n > 6) throw new Error("n must be an integer from 1 to 6."); + const watermark = args.watermark ?? false; + if (typeof watermark !== "boolean") throw new Error("watermark must be a boolean."); + const payload = { model, prompt: args.prompt, size, n, watermark, async: true }; + if (args.image_urls !== undefined) { + if (!Array.isArray(args.image_urls) || args.image_urls.length < 1 || args.image_urls.length > 3 || + args.image_urls.some((url) => typeof url !== "string" || !/^https:\/\//.test(url))) { + throw new Error("image_urls must contain one to three HTTPS URLs."); + } + payload.image_urls = args.image_urls; + } + return payload; +} + +export async function callTool(name, args) { + try { + if (name === "qwen_image_generate") { + const body = await post("/qwen-image/images", generationPayload(args), 90000); + const taskId = body?.task_id || body?.task?.id || body?.response?.task_id; + if (!taskId) { + return textResult({ status: "unknown", message: "Generation response had no task ID. Check usage history before resubmitting." }, true); + } + return textResult({ status: "pending", task_id: String(taskId), trace_id: String(body?.trace_id || "") }); + } + if (name === "qwen_image_get_task") { + const taskId = args?.task_id; + if (typeof taskId !== "string" || !taskId.trim()) throw new Error("task_id is required."); + const body = await post(TASK_PATH, { action: "retrieve", id: taskId }, 30000); + return textResult(normalizeTask(body, taskId)); + } + return textResult({ error: "Unknown tool." }, true); + } catch (error) { + return textResult({ error: error instanceof Error ? error.message : "Tool failed." }, true); + } +} + +export async function handle(message) { + if (!message || typeof message !== "object" || !("id" in message)) return null; + let result; + switch (message.method) { + case "initialize": + result = { + protocolVersion: "2025-03-26", + capabilities: { tools: {} }, + serverInfo: { name: "qwen-image-acedatacloud", version: "1.0.0" } + }; + break; + case "ping": + result = {}; + break; + case "tools/list": + result = { tools }; + break; + case "tools/call": + result = await callTool(message.params?.name, message.params?.arguments || {}); + break; + default: + return { jsonrpc: "2.0", id: message.id, error: { code: -32601, message: "Method not found" } }; + } + return { jsonrpc: "2.0", id: message.id, result }; +} + +if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) { + const reader = createInterface({ input: stdin, crlfDelay: Infinity }); + let queue = Promise.resolve(); + reader.on("line", (line) => { + queue = queue.then(async () => { + let message; + try { message = JSON.parse(line); } catch { return; } + const response = await handle(message); + if (response) stdout.write(JSON.stringify(response) + "\n"); + }).catch(() => {}); + }); +} diff --git a/third_party/qwen-image/skills/qwen-image/SKILL.md b/third_party/qwen-image/skills/qwen-image/SKILL.md new file mode 100644 index 000000000..18de4e717 --- /dev/null +++ b/third_party/qwen-image/skills/qwen-image/SKILL.md @@ -0,0 +1,15 @@ +--- +name: qwen-image +description: Generate or edit Qwen Image pictures through Ace Data Cloud in Cursor and resume an existing Qwen Image task. Use when the user asks for a Qwen Image result. +--- + +# Qwen Image in Cursor + +This plugin supplies a small local MCP server because the existing Ace Data Cloud MCP catalog has no dedicated Qwen Image server. Its request fields follow the current Qwen Image API contract used by the Ace Data Cloud Dify integration. + +1. Check [current pricing and balance](https://platform.acedata.cloud/models) and confirm the account has Qwen Image access. +2. Call `qwen_image_generate` once with a prompt and a supported model. Keep the returned `task_id`. This submits a paid request even if the task is still pending. +3. Call `qwen_image_get_task` with that same ID until status is succeeded or failed. After an interruption, resume with the old ID; never generate again just to poll. +4. Open the completed HTTPS image URL and report the task ID and result. A pending response is not a finished image. + +The API key is configured in **Customize → Plugins → Qwen Image → Configure** as `ACEDATACLOUD_API_TOKEN`. Do not place it in chat or files. See the [first-run guide](../../README.md). diff --git a/third_party/qwen-image/tests/protocol.test.mjs b/third_party/qwen-image/tests/protocol.test.mjs new file mode 100644 index 000000000..6ac1badf9 --- /dev/null +++ b/third_party/qwen-image/tests/protocol.test.mjs @@ -0,0 +1,47 @@ +import test from "node:test"; +import assert from "node:assert/strict"; +import { handle, normalizeTask } from "../server.mjs"; + +test("MCP advertises the generation and same-task query tools", async () => { + const init = await handle({ jsonrpc: "2.0", id: 1, method: "initialize", params: {} }); + assert.equal(init.result.serverInfo.name, "qwen-image-acedatacloud"); + assert.deepEqual(init.result.capabilities, { tools: {} }); + const list = await handle({ jsonrpc: "2.0", id: 2, method: "tools/list", params: {} }); + assert.deepEqual(list.result.tools.map((item) => item.name), [ + "qwen_image_generate", + "qwen_image_get_task" + ]); +}); + +test("unfinished tasks stay pending even with an intermediate response", () => { + const task = normalizeTask({ + id: "same-task", + finished_at: null, + response: { success: false, data: [{ image_url: "https://example.com/intermediate.png" }] } + }); + assert.equal(task.status, "pending"); + assert.equal(task.task_id, "same-task"); + assert.deepEqual(task.media_urls, []); +}); + +test("terminal success exposes only completed HTTPS images", () => { + const task = normalizeTask({ + id: "same-task", + finished_at: 1, + trace_id: "trace", + response: { + success: true, + data: [{ image_url: "https://example.com/final.png" }, { image_url: "http://example.com/unsafe.png" }] + } + }); + assert.equal(task.status, "succeeded"); + assert.equal(task.trace_id, "trace"); + assert.deepEqual(task.media_urls, ["https://example.com/final.png"]); +}); + +test("terminal failure is not reported as pending or successful", () => { + const task = normalizeTask({ id: "same-task", finished_at: 1, response: { success: false } }); + assert.equal(task.status, "failed"); + assert.equal(task.success, false); + assert.deepEqual(task.media_urls, []); +}); From f472ce8ea79c6c1215f5ee4a67cbf2c119f712bc Mon Sep 17 00:00:00 2001 From: Ace Data Cloud Dev Date: Mon, 12 Oct 2026 02:40:06 +0800 Subject: [PATCH 2/4] refactor: reuse hosted Qwen Image MCP --- .../qwen-image/.cursor-plugin/plugin.json | 2 +- third_party/qwen-image/CHANGELOG.md | 4 + third_party/qwen-image/README.md | 28 ++- .../qwen-image/examples/first-image.md | 2 +- third_party/qwen-image/mcp.json | 10 +- .../qwen-image/readme/README_zh_Hans.md | 16 +- third_party/qwen-image/server.mjs | 189 ------------------ .../qwen-image/skills/qwen-image/SKILL.md | 10 +- .../qwen-image/tests/protocol.test.mjs | 47 ----- 9 files changed, 41 insertions(+), 267 deletions(-) delete mode 100644 third_party/qwen-image/server.mjs delete mode 100644 third_party/qwen-image/tests/protocol.test.mjs diff --git a/third_party/qwen-image/.cursor-plugin/plugin.json b/third_party/qwen-image/.cursor-plugin/plugin.json index 5f4ad7888..c1d206cf2 100644 --- a/third_party/qwen-image/.cursor-plugin/plugin.json +++ b/third_party/qwen-image/.cursor-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "qwen-image", "displayName": "Qwen Image", - "version": "1.0.0", + "version": "1.0.1", "minClientVersions": { "cursor": "3.13.0" }, diff --git a/third_party/qwen-image/CHANGELOG.md b/third_party/qwen-image/CHANGELOG.md index 78b59af41..a80dfa912 100644 --- a/third_party/qwen-image/CHANGELOG.md +++ b/third_party/qwen-image/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## 1.0.1 + +- Reuse the existing hosted Qwen Image MCP instead of bundling a local Node server. The hosted server exposes generation, editing, model listing, and same-task retrieval tools. + ## 1.0.0 - Initial Qwen Image Cursor plugin with a small local MCP server for generation and same-task retrieval. diff --git a/third_party/qwen-image/README.md b/third_party/qwen-image/README.md index 1d8b46d98..f31dcbecb 100644 --- a/third_party/qwen-image/README.md +++ b/third_party/qwen-image/README.md @@ -1,13 +1,11 @@ # Qwen Image for Cursor -Generate and edit Qwen Image pictures through [Ace Data Cloud](https://platform.acedata.cloud) in Cursor. This focused plugin ships a dependency-free local MCP server with generation and same-task retrieval tools. It uses the current Qwen Image API contract already verified by the Ace Data Cloud Dify integration. +Generate and edit Qwen Image pictures through [Ace Data Cloud](https://platform.acedata.cloud) in Cursor. This focused plugin reuses the existing hosted Qwen Image MCP for generation, editing, model listing, and same-task retrieval. The live MCP schemas define available actions and arguments. [简体中文教程](readme/README_zh_Hans.md) · [Copyable first-run prompt](examples/first-image.md) · [Current pricing](https://platform.acedata.cloud/models) ## 1. Install -Install [Node.js 20 or later](https://nodejs.org/en/download) and check `node --version` in a terminal. The plugin starts its local MCP server with `node`; it does not download packages or run a remote executable. - In Cursor 3.24, open **Customize → Browse Marketplace**, search for **Qwen Image**, confirm the **Ace Data Cloud Dev** author and Qwen Image logo, then click **Add**. Earlier Cursor versions may put Plugins under Settings. If the official listing is not live, copy this directory to `~/.cursor/plugins/local/qwen-image/`, then open **Customize → Plugins** and confirm the **Local** label. Local installation does not prove Marketplace publication. ## 2. Get the right API key @@ -20,15 +18,27 @@ Use a service API key, not a platform management token. Do not put it in prompts ## 3. Configure the plugin -Open **Customize → Plugins → Qwen Image → Configure**, paste the key string into **Ace Data Cloud API token**, and save. Team administrators can configure the same variable in **Dashboard → Plugins → Configure**. The plugin passes the configured value to its local MCP server as `ACEDATACLOUD_API_TOKEN` and adds the Bearer header itself. +Open **Customize → Plugins → Qwen Image → Configure**, paste the key string into **Ace Data Cloud API token**, and click **Save**. Wait for Save to finish, then reopen Configure to confirm a masked value. Team administrators can configure the same variable in **Dashboard → Plugins → Configure**. Cursor uses `ACEDATACLOUD_API_TOKEN` in the hosted MCP Authorization header; the plugin adds `Bearer ` itself. + +The transport is hosted HTTP; no local Node server is required: -The bundled `mcp.json` starts `server.mjs` from `CURSOR_PLUGIN_ROOT`. It uses Node's built-in fetch and no npm packages. If the MCP does not start, check that `node` is on Cursor's process PATH and reconnect the plugin. +```json +{ + "mcpServers": { + "qwen-image": { + "type": "http", + "url": "https://qwen-image.mcp.acedata.cloud/mcp", + "headers": {"Authorization": "Bearer ${ACEDATACLOUD_API_TOKEN}"} + } + } +} +``` ## 4. Make and retrieve the first image -Paste the [first-image prompt](examples/first-image.md) into Cursor Agent. It requests one `qwen_image_generate` call with model `qwen-image-3.0`, size `1024*1024`, count `1`, and no watermark. Use a model available to your account; the current service contract is the source of truth. +Paste the [first-image prompt](examples/first-image.md) into Cursor Agent. It checks `qwen_image_list_models`, then requests one `qwen_image_generate` call with model `qwen-image-3.0` when available, size `1024*1024`, count `1`, and no watermark. Use the live tool schema for supported values. -The paid generation returns a `task_id` while work may still be pending. Keep it. Call `qwen_image_get_task` with that **same** ID until `status=succeeded` and an image URL appears. If the chat is interrupted, begin a new chat with “Query Qwen Image task ; do not generate again.” Open the returned HTTPS PNG and check the image. +The paid generation returns a task ID while work may still be pending. Keep it. Call `qwen_image_get_task` with that **same** ID until `response.success` is true or false; a successful result contains `response.data[].image_url`. If the chat is interrupted, begin a new chat with “Query Qwen Image task ; do not generate again.” Open the returned HTTPS image and check it. In **Ace Data Cloud → Usage/Billing**, match the one generation to its deducted Credits record. Task queries do not submit another generation. The plugin is free; API calls follow current service pricing. @@ -37,8 +47,8 @@ In **Ace Data Cloud → Usage/Billing**, match the one generation to its deducte | Symptom | Action | | --- | --- | | Plugin not listed | Check the exact Qwen Image entry and author. A local plugin is separate from the public Marketplace. | -| MCP server will not start | Run `node --version`, confirm Node 20+ is on Cursor's PATH, and reconnect the plugin. | -| 401 or 403 | Check the service key, expiration, Qwen Image access, balance, and both Allowed APIs. Paste only the key, without `Bearer `. | +| MCP is not connected | Check the saved key and hosted endpoint, then reconnect the plugin. The plugin needs no local runtime. | +| 401 or 403 | Check the service key, expiration, Qwen Image access, balance, and both Allowed APIs. Paste only the key, without `Bearer `. If the same key works through the Ace API but Cursor reports an invalid token, confirm Save finished and restart Cursor before retrying a read-only task query. | | Invalid model or size | Use a currently supported model and `WIDTH*HEIGHT` size such as `1024*1024`. | | Pending or interrupted task | Query the original task ID; do not regenerate to poll. | | Timeout or 5xx | Check the task and usage history before deciding whether another paid request is needed. | diff --git a/third_party/qwen-image/examples/first-image.md b/third_party/qwen-image/examples/first-image.md index e999a8368..bf813a754 100644 --- a/third_party/qwen-image/examples/first-image.md +++ b/third_party/qwen-image/examples/first-image.md @@ -2,4 +2,4 @@ Copy this into Cursor Agent after installing the plugin and configuring its API key. The prompt contains no credential. -> Use the Qwen Image plugin to make one 1024×1024 image of a single blue paper sphere on a cream background, as a clean studio product photograph with no text. Use `qwen-image-3.0`, `n: 1`, and `watermark: false` if available to this account. Call `qwen_image_generate` only once, save its task ID, then use `qwen_image_get_task` with the same ID until it succeeds or fails. Open the completed PNG URL and tell me the original task ID. If it is still pending, give me that ID so I can resume without another generation. +> Use the Qwen Image plugin to make one 1024×1024 image of a single blue paper sphere on a cream background, as a clean studio product photograph with no text. Check `qwen_image_list_models`; use `qwen-image-3.0`, `n: 1`, and `watermark: false` if available to this account. Call `qwen_image_generate` only once, save its task ID, then use `qwen_image_get_task` with the same ID until `response.success` is true or false. Open the returned HTTPS image URL and tell me the original task ID. If it is still pending, give me that ID so I can resume without another generation. diff --git a/third_party/qwen-image/mcp.json b/third_party/qwen-image/mcp.json index dcbddef17..fb6cddd65 100644 --- a/third_party/qwen-image/mcp.json +++ b/third_party/qwen-image/mcp.json @@ -1,12 +1,10 @@ { "mcpServers": { "qwen-image": { - "type": "stdio", - "command": "node", - "args": ["${CURSOR_PLUGIN_ROOT}/server.mjs"], - "cwd": "${CURSOR_PLUGIN_ROOT}", - "env": { - "ACEDATACLOUD_API_TOKEN": "${ACEDATACLOUD_API_TOKEN}" + "type": "http", + "url": "https://qwen-image.mcp.acedata.cloud/mcp", + "headers": { + "Authorization": "Bearer ${ACEDATACLOUD_API_TOKEN}" } } } diff --git a/third_party/qwen-image/readme/README_zh_Hans.md b/third_party/qwen-image/readme/README_zh_Hans.md index d8964a6fa..ad05c9037 100644 --- a/third_party/qwen-image/readme/README_zh_Hans.md +++ b/third_party/qwen-image/readme/README_zh_Hans.md @@ -1,13 +1,11 @@ # Cursor Qwen Image 插件 -通过 [Ace Data Cloud](https://platform.acedata.cloud) 在 Cursor 中生成或编辑 Qwen Image 图片。插件自带无第三方依赖的本地 MCP 服务,提供生成和同 task ID 查询工具;请求参数沿用 Ace Data Cloud Dify 集成已验证的当前服务契约。 +通过 [Ace Data Cloud](https://platform.acedata.cloud) 在 Cursor 中生成或编辑 Qwen Image 图片。插件复用现有托管 Qwen Image MCP,提供生成、编辑、模型列表及同任务查询工具;可用参数以在线工具 schema 为准。 [English guide](../README.md) · [可复制的首跑提示词](../examples/first-image.md) · [当前价格](https://platform.acedata.cloud/models) ## 1. 安装 -先安装 [Node.js 20 或更新版本](https://nodejs.org/en/download),并在终端运行 `node --version`。插件用 `node` 启动本地 MCP,不下载 npm 包或远端可执行程序。 - 在 Cursor 3.24 打开 **Customize → Browse Marketplace**,搜索 **Qwen Image**,确认作者 **Ace Data Cloud Dev** 和服务图标,再点击 **Add**。较早版本可能将 Plugins 放在 Settings。官方条目尚未出现时,可把本目录复制到 `~/.cursor/plugins/local/qwen-image/`,再到 **Customize → Plugins** 查看 **Local** 标签;本地安装不代表 Marketplace 已上架。 ## 2. 获取服务 API Key @@ -20,15 +18,15 @@ ## 3. 配置插件 -打开 **Customize → Plugins → Qwen Image → Configure**,将 Key 原文填入 **Ace Data Cloud API token** 并保存。团队管理员也可通过 **Dashboard → Plugins → Configure** 配置同一变量。插件把值作为 `ACEDATACLOUD_API_TOKEN` 传给本地 MCP,并自行加上 Bearer 请求头。 +打开 **Customize → Plugins → Qwen Image → Configure**,将 Key 原文填入 **Ace Data Cloud API token** 并点击 **Save**。等待保存完成,重新打开 Configure,确认字段显示遮蔽值。团队管理员也可通过 **Dashboard → Plugins → Configure** 配置同一变量。Cursor 将 `ACEDATACLOUD_API_TOKEN` 用于托管 MCP 的 Authorization 请求头;插件自行加上 `Bearer ` 前缀。 -`mcp.json` 从 `CURSOR_PLUGIN_ROOT` 启动 `server.mjs`,只使用 Node 内置 fetch。若 MCP 没有启动,检查 Cursor 进程 PATH 中能找到 `node`,然后重连插件。 +插件通过 `https://qwen-image.mcp.acedata.cloud/mcp` 连接托管 HTTP MCP,无需本地 Node 服务。若工具没有出现,检查 Key 并重连插件。 ## 4. 生成并取回首张图片 -在 Cursor Agent 中复制[首跑提示词](../examples/first-image.md)。示例只调用一次 `qwen_image_generate`,使用账户可用时的 `qwen-image-3.0`、尺寸 `1024*1024`、数量 `1`、无水印。实际可用模型以当前服务契约为准。 +在 Cursor Agent 中复制[首跑提示词](../examples/first-image.md)。它先查询 `qwen_image_list_models`,再调用一次 `qwen_image_generate`;账户可用时选 `qwen-image-3.0`、尺寸 `1024*1024`、数量 `1`、无水印。实际参数以在线工具 schema 为准。 -付费生成可能先返回 `task_id`,此时图片仍在处理。保存这个 ID,沿同一 ID 调用 `qwen_image_get_task`,直到 `status=succeeded` 并返回图片地址。聊天中断时新开聊天说“查询 Qwen Image 任务 ,不要重新生成”。打开最终 HTTPS PNG 检查图片。 +付费生成可能先返回 task ID,此时图片仍在处理。保存这个 ID,沿同一 ID 调用 `qwen_image_get_task`,直到 `response.success` 为 true 或 false;成功结果包含 `response.data[].image_url`。聊天中断时新开聊天说“查询 Qwen Image 任务 ,不要重新生成”。打开最终 HTTPS 图片检查。 到 **Ace Data Cloud → Usage/Billing** 核对这一次生成对应的 Credits 扣费。任务查询不会再提交生成。插件免费,API 调用按当前服务价格计费。 @@ -37,8 +35,8 @@ | 现象 | 处理 | | --- | --- | | 找不到插件 | 核对 Qwen Image 条目和作者;本地插件与官方市场是两条渠道。 | -| MCP 未启动 | 运行 `node --version`,确认 Node 20+ 在 Cursor 的 PATH 中,再重连。 | -| 401 或 403 | 检查服务 Key、有效期、权限、余额和两个 Allowed APIs;配置值不要带 `Bearer `。 | +| MCP 未连接 | 检查保存的 Key 与托管端点,重连插件;无需本地运行时。 | +| 401 或 403 | 检查服务 Key、有效期、权限、余额和两个 Allowed APIs;配置值不要带 `Bearer `。若同一 Key 直连 Ace API 成功而 Cursor 仍报 token invalid,确认 Save 已完成,重启 Cursor 后先重试旧任务只读查询。 | | 模型或尺寸错误 | 用当前支持的模型,尺寸写成 `WIDTH*HEIGHT`,例如 `1024*1024`。 | | pending 或任务中断 | 沿原 task ID 查询,不要为轮询再次生成。 | | 超时或 5xx | 先检查原任务和用量记录,再决定是否需要另一笔付费请求。 | diff --git a/third_party/qwen-image/server.mjs b/third_party/qwen-image/server.mjs deleted file mode 100644 index e9a3793c1..000000000 --- a/third_party/qwen-image/server.mjs +++ /dev/null @@ -1,189 +0,0 @@ -import { createInterface } from "node:readline"; -import { stdin, stdout } from "node:process"; -import { pathToFileURL } from "node:url"; - -const API_ORIGIN = "https://api.acedata.cloud"; -const MODELS = ["qwen-image-3.0", "qwen-image-3.0-pro"]; -const TASK_PATH = "/qwen-image/tasks"; - -export const tools = [ - { - name: "qwen_image_generate", - description: "Submit one Qwen Image generation or edit through Ace Data Cloud. This is a paid operation. Save the returned task_id and query that same task; do not call generate again to poll.", - inputSchema: { - type: "object", - properties: { - prompt: { type: "string", minLength: 1, maxLength: 18000, description: "Describe the desired image or edit." }, - model: { type: "string", enum: MODELS, default: "qwen-image-3.0" }, - size: { type: "string", pattern: "^[0-9]+\\*[0-9]+$", default: "1024*1024" }, - n: { type: "integer", minimum: 1, maximum: 6, default: 1 }, - watermark: { type: "boolean", default: false }, - image_urls: { - type: "array", - minItems: 1, - maxItems: 3, - items: { type: "string", format: "uri" }, - description: "Optional reference image URLs for editing." - } - }, - required: ["prompt"] - }, - annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: true } - }, - { - name: "qwen_image_get_task", - description: "Query an existing Qwen Image task by the original task_id. This does not submit another generation.", - inputSchema: { - type: "object", - properties: { - task_id: { type: "string", minLength: 1, description: "The ID returned by qwen_image_generate." } - }, - required: ["task_id"] - }, - annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: true } - } -]; - -function textResult(value, isError = false) { - return { content: [{ type: "text", text: JSON.stringify(value) }], isError }; -} - -function requireToken() { - const token = process.env.ACEDATACLOUD_API_TOKEN; - if (!token) throw new Error("Configure ACEDATACLOUD_API_TOKEN in the Cursor plugin."); - return token; -} - -async function post(path, payload, timeoutMs) { - const token = requireToken(); - let response; - try { - response = await fetch(API_ORIGIN + path, { - method: "POST", - headers: { - Authorization: "Bearer " + token, - "Content-Type": "application/json" - }, - body: JSON.stringify(payload), - signal: AbortSignal.timeout(timeoutMs) - }); - } catch { - throw new Error("Network request failed or timed out. Check task and usage history before any new paid submission."); - } - let data; - try { - data = await response.json(); - } catch { - throw new Error("Ace Data Cloud returned a non-JSON response (HTTP " + response.status + ")."); - } - if (!response.ok) { - const traceId = typeof data?.trace_id === "string" ? data.trace_id : ""; - throw new Error("Ace Data Cloud request failed (HTTP " + response.status + ")" + (traceId ? "; trace_id " + traceId : "") + "."); - } - return data; -} - -export function normalizeTask(body, requestedId = "") { - const taskId = String(body?.id || body?.task_id || body?.response?.task_id || requestedId); - const response = body?.response && typeof body.response === "object" ? body.response : {}; - const data = Array.isArray(response.data) ? response.data : []; - const mediaUrls = data - .map((item) => item && (item.image_url || item.url)) - .filter((url) => typeof url === "string" && /^https:\/\//.test(url)); - const unfinished = body?.finished_at === null; - const succeeded = !unfinished && response.success === true; - const failed = !unfinished && response.success === false; - return { - status: succeeded ? "succeeded" : failed ? "failed" : "pending", - success: succeeded, - task_id: taskId, - trace_id: typeof body?.trace_id === "string" ? body.trace_id : "", - media_urls: succeeded ? mediaUrls : [] - }; -} - -function generationPayload(args) { - if (!args || typeof args.prompt !== "string" || !args.prompt.trim() || args.prompt.length > 18000) { - throw new Error("prompt must contain 1 to 18000 characters."); - } - const model = args.model ?? "qwen-image-3.0"; - if (!MODELS.includes(model)) throw new Error("Unsupported Qwen Image model."); - const size = args.size ?? "1024*1024"; - if (typeof size !== "string" || !/^[0-9]+\*[0-9]+$/.test(size)) { - throw new Error("size must be WIDTH*HEIGHT, for example 1024*1024."); - } - const n = args.n ?? 1; - if (!Number.isInteger(n) || n < 1 || n > 6) throw new Error("n must be an integer from 1 to 6."); - const watermark = args.watermark ?? false; - if (typeof watermark !== "boolean") throw new Error("watermark must be a boolean."); - const payload = { model, prompt: args.prompt, size, n, watermark, async: true }; - if (args.image_urls !== undefined) { - if (!Array.isArray(args.image_urls) || args.image_urls.length < 1 || args.image_urls.length > 3 || - args.image_urls.some((url) => typeof url !== "string" || !/^https:\/\//.test(url))) { - throw new Error("image_urls must contain one to three HTTPS URLs."); - } - payload.image_urls = args.image_urls; - } - return payload; -} - -export async function callTool(name, args) { - try { - if (name === "qwen_image_generate") { - const body = await post("/qwen-image/images", generationPayload(args), 90000); - const taskId = body?.task_id || body?.task?.id || body?.response?.task_id; - if (!taskId) { - return textResult({ status: "unknown", message: "Generation response had no task ID. Check usage history before resubmitting." }, true); - } - return textResult({ status: "pending", task_id: String(taskId), trace_id: String(body?.trace_id || "") }); - } - if (name === "qwen_image_get_task") { - const taskId = args?.task_id; - if (typeof taskId !== "string" || !taskId.trim()) throw new Error("task_id is required."); - const body = await post(TASK_PATH, { action: "retrieve", id: taskId }, 30000); - return textResult(normalizeTask(body, taskId)); - } - return textResult({ error: "Unknown tool." }, true); - } catch (error) { - return textResult({ error: error instanceof Error ? error.message : "Tool failed." }, true); - } -} - -export async function handle(message) { - if (!message || typeof message !== "object" || !("id" in message)) return null; - let result; - switch (message.method) { - case "initialize": - result = { - protocolVersion: "2025-03-26", - capabilities: { tools: {} }, - serverInfo: { name: "qwen-image-acedatacloud", version: "1.0.0" } - }; - break; - case "ping": - result = {}; - break; - case "tools/list": - result = { tools }; - break; - case "tools/call": - result = await callTool(message.params?.name, message.params?.arguments || {}); - break; - default: - return { jsonrpc: "2.0", id: message.id, error: { code: -32601, message: "Method not found" } }; - } - return { jsonrpc: "2.0", id: message.id, result }; -} - -if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) { - const reader = createInterface({ input: stdin, crlfDelay: Infinity }); - let queue = Promise.resolve(); - reader.on("line", (line) => { - queue = queue.then(async () => { - let message; - try { message = JSON.parse(line); } catch { return; } - const response = await handle(message); - if (response) stdout.write(JSON.stringify(response) + "\n"); - }).catch(() => {}); - }); -} diff --git a/third_party/qwen-image/skills/qwen-image/SKILL.md b/third_party/qwen-image/skills/qwen-image/SKILL.md index 18de4e717..521634a30 100644 --- a/third_party/qwen-image/skills/qwen-image/SKILL.md +++ b/third_party/qwen-image/skills/qwen-image/SKILL.md @@ -5,11 +5,11 @@ description: Generate or edit Qwen Image pictures through Ace Data Cloud in Curs # Qwen Image in Cursor -This plugin supplies a small local MCP server because the existing Ace Data Cloud MCP catalog has no dedicated Qwen Image server. Its request fields follow the current Qwen Image API contract used by the Ace Data Cloud Dify integration. +Use the existing Ace Data Cloud hosted Qwen Image MCP. Its live tool schemas are the source of truth for available models, arguments, and results. -1. Check [current pricing and balance](https://platform.acedata.cloud/models) and confirm the account has Qwen Image access. -2. Call `qwen_image_generate` once with a prompt and a supported model. Keep the returned `task_id`. This submits a paid request even if the task is still pending. -3. Call `qwen_image_get_task` with that same ID until status is succeeded or failed. After an interruption, resume with the old ID; never generate again just to poll. -4. Open the completed HTTPS image URL and report the task ID and result. A pending response is not a finished image. +1. Check [current pricing and balance](https://platform.acedata.cloud/models), confirm Qwen Image access, and use `qwen_image_list_models` when the requested model is unclear. +2. Call `qwen_image_generate` or `qwen_image_edit` once for the requested paid action. Keep the returned task ID even if the result is still pending. +3. Call `qwen_image_get_task` with that same ID until `response.success` is true or false. After an interruption, resume with the old ID; never generate again just to poll. +4. On success, open a returned `response.data[].image_url` HTTPS link and report the task ID and result. A pending response is not a finished image. The API key is configured in **Customize → Plugins → Qwen Image → Configure** as `ACEDATACLOUD_API_TOKEN`. Do not place it in chat or files. See the [first-run guide](../../README.md). diff --git a/third_party/qwen-image/tests/protocol.test.mjs b/third_party/qwen-image/tests/protocol.test.mjs deleted file mode 100644 index 6ac1badf9..000000000 --- a/third_party/qwen-image/tests/protocol.test.mjs +++ /dev/null @@ -1,47 +0,0 @@ -import test from "node:test"; -import assert from "node:assert/strict"; -import { handle, normalizeTask } from "../server.mjs"; - -test("MCP advertises the generation and same-task query tools", async () => { - const init = await handle({ jsonrpc: "2.0", id: 1, method: "initialize", params: {} }); - assert.equal(init.result.serverInfo.name, "qwen-image-acedatacloud"); - assert.deepEqual(init.result.capabilities, { tools: {} }); - const list = await handle({ jsonrpc: "2.0", id: 2, method: "tools/list", params: {} }); - assert.deepEqual(list.result.tools.map((item) => item.name), [ - "qwen_image_generate", - "qwen_image_get_task" - ]); -}); - -test("unfinished tasks stay pending even with an intermediate response", () => { - const task = normalizeTask({ - id: "same-task", - finished_at: null, - response: { success: false, data: [{ image_url: "https://example.com/intermediate.png" }] } - }); - assert.equal(task.status, "pending"); - assert.equal(task.task_id, "same-task"); - assert.deepEqual(task.media_urls, []); -}); - -test("terminal success exposes only completed HTTPS images", () => { - const task = normalizeTask({ - id: "same-task", - finished_at: 1, - trace_id: "trace", - response: { - success: true, - data: [{ image_url: "https://example.com/final.png" }, { image_url: "http://example.com/unsafe.png" }] - } - }); - assert.equal(task.status, "succeeded"); - assert.equal(task.trace_id, "trace"); - assert.deepEqual(task.media_urls, ["https://example.com/final.png"]); -}); - -test("terminal failure is not reported as pending or successful", () => { - const task = normalizeTask({ id: "same-task", finished_at: 1, response: { success: false } }); - assert.equal(task.status, "failed"); - assert.equal(task.success, false); - assert.deepEqual(task.media_urls, []); -}); From da077c1f33d298837376658bc8f26d6abfb5257a Mon Sep 17 00:00:00 2001 From: Ace Data Cloud Dev Date: Mon, 12 Oct 2026 03:03:44 +0800 Subject: [PATCH 3/4] docs: keep unfinished Qwen tasks polling --- third_party/qwen-image/CHANGELOG.md | 1 + third_party/qwen-image/README.md | 2 +- third_party/qwen-image/examples/first-image.md | 2 +- third_party/qwen-image/readme/README_zh_Hans.md | 2 +- third_party/qwen-image/skills/qwen-image/SKILL.md | 2 +- 5 files changed, 5 insertions(+), 4 deletions(-) diff --git a/third_party/qwen-image/CHANGELOG.md b/third_party/qwen-image/CHANGELOG.md index a80dfa912..bf0b8023b 100644 --- a/third_party/qwen-image/CHANGELOG.md +++ b/third_party/qwen-image/CHANGELOG.md @@ -3,6 +3,7 @@ ## 1.0.1 - Reuse the existing hosted Qwen Image MCP instead of bundling a local Node server. The hosted server exposes generation, editing, model listing, and same-task retrieval tools. +- Clarify that `finished_at: null` remains pending even if an interim response says `success: false`. ## 1.0.0 diff --git a/third_party/qwen-image/README.md b/third_party/qwen-image/README.md index f31dcbecb..8639cdcbc 100644 --- a/third_party/qwen-image/README.md +++ b/third_party/qwen-image/README.md @@ -38,7 +38,7 @@ The transport is hosted HTTP; no local Node server is required: Paste the [first-image prompt](examples/first-image.md) into Cursor Agent. It checks `qwen_image_list_models`, then requests one `qwen_image_generate` call with model `qwen-image-3.0` when available, size `1024*1024`, count `1`, and no watermark. Use the live tool schema for supported values. -The paid generation returns a task ID while work may still be pending. Keep it. Call `qwen_image_get_task` with that **same** ID until `response.success` is true or false; a successful result contains `response.data[].image_url`. If the chat is interrupted, begin a new chat with “Query Qwen Image task ; do not generate again.” Open the returned HTTPS image and check it. +The paid generation returns a task ID while work may still be pending. Keep it. Call `qwen_image_get_task` with that **same** ID until `finished_at` is non-null. While `finished_at` is null, continue querying even if `response.success` is false. Once finished, `response.success: true` includes `response.data[].image_url`; `false` is a terminal failure. If the chat is interrupted, begin a new chat with “Query Qwen Image task ; do not generate again.” Open the returned HTTPS image and check it. In **Ace Data Cloud → Usage/Billing**, match the one generation to its deducted Credits record. Task queries do not submit another generation. The plugin is free; API calls follow current service pricing. diff --git a/third_party/qwen-image/examples/first-image.md b/third_party/qwen-image/examples/first-image.md index bf813a754..3f5e7323c 100644 --- a/third_party/qwen-image/examples/first-image.md +++ b/third_party/qwen-image/examples/first-image.md @@ -2,4 +2,4 @@ Copy this into Cursor Agent after installing the plugin and configuring its API key. The prompt contains no credential. -> Use the Qwen Image plugin to make one 1024×1024 image of a single blue paper sphere on a cream background, as a clean studio product photograph with no text. Check `qwen_image_list_models`; use `qwen-image-3.0`, `n: 1`, and `watermark: false` if available to this account. Call `qwen_image_generate` only once, save its task ID, then use `qwen_image_get_task` with the same ID until `response.success` is true or false. Open the returned HTTPS image URL and tell me the original task ID. If it is still pending, give me that ID so I can resume without another generation. +> Use the Qwen Image plugin to make one 1024×1024 image of a single blue paper sphere on a cream background, as a clean studio product photograph with no text. Check `qwen_image_list_models`; use `qwen-image-3.0`, `n: 1`, and `watermark: false` if available to this account. Call `qwen_image_generate` only once, save its task ID, then use `qwen_image_get_task` with the same ID until `finished_at` is non-null. A `response.success: false` while `finished_at` is null is still pending; do not generate again to poll. On completion, open the returned HTTPS image URL and tell me the original task ID. If it is still pending, give me that ID so I can resume. diff --git a/third_party/qwen-image/readme/README_zh_Hans.md b/third_party/qwen-image/readme/README_zh_Hans.md index ad05c9037..6e5132b4c 100644 --- a/third_party/qwen-image/readme/README_zh_Hans.md +++ b/third_party/qwen-image/readme/README_zh_Hans.md @@ -26,7 +26,7 @@ 在 Cursor Agent 中复制[首跑提示词](../examples/first-image.md)。它先查询 `qwen_image_list_models`,再调用一次 `qwen_image_generate`;账户可用时选 `qwen-image-3.0`、尺寸 `1024*1024`、数量 `1`、无水印。实际参数以在线工具 schema 为准。 -付费生成可能先返回 task ID,此时图片仍在处理。保存这个 ID,沿同一 ID 调用 `qwen_image_get_task`,直到 `response.success` 为 true 或 false;成功结果包含 `response.data[].image_url`。聊天中断时新开聊天说“查询 Qwen Image 任务 ,不要重新生成”。打开最终 HTTPS 图片检查。 +付费生成可能先返回 task ID,此时图片仍在处理。保存这个 ID,沿同一 ID 调用 `qwen_image_get_task`,直到 `finished_at` 非空。`finished_at` 为 null 时,即使 `response.success` 为 false 也要继续查同一任务;结束后 `response.success: true` 的结果包含 `response.data[].image_url`,false 才是终态失败。聊天中断时新开聊天说“查询 Qwen Image 任务 ,不要重新生成”。打开最终 HTTPS 图片检查。 到 **Ace Data Cloud → Usage/Billing** 核对这一次生成对应的 Credits 扣费。任务查询不会再提交生成。插件免费,API 调用按当前服务价格计费。 diff --git a/third_party/qwen-image/skills/qwen-image/SKILL.md b/third_party/qwen-image/skills/qwen-image/SKILL.md index 521634a30..485788b2d 100644 --- a/third_party/qwen-image/skills/qwen-image/SKILL.md +++ b/third_party/qwen-image/skills/qwen-image/SKILL.md @@ -9,7 +9,7 @@ Use the existing Ace Data Cloud hosted Qwen Image MCP. Its live tool schemas are 1. Check [current pricing and balance](https://platform.acedata.cloud/models), confirm Qwen Image access, and use `qwen_image_list_models` when the requested model is unclear. 2. Call `qwen_image_generate` or `qwen_image_edit` once for the requested paid action. Keep the returned task ID even if the result is still pending. -3. Call `qwen_image_get_task` with that same ID until `response.success` is true or false. After an interruption, resume with the old ID; never generate again just to poll. +3. Call `qwen_image_get_task` with that same ID until `finished_at` is non-null. An interim `response.success: false` with `finished_at: null` is still pending. Once finished, inspect `response.success`; after an interruption, resume with the old ID and never generate again just to poll. 4. On success, open a returned `response.data[].image_url` HTTPS link and report the task ID and result. A pending response is not a finished image. The API key is configured in **Customize → Plugins → Qwen Image → Configure** as `ACEDATACLOUD_API_TOKEN`. Do not place it in chat or files. See the [first-run guide](../../README.md). From c34587e6f4487a66cd9f786fe7bf26aeaf89b037 Mon Sep 17 00:00:00 2001 From: Ace Data Cloud Dev Date: Mon, 12 Oct 2026 05:28:36 +0000 Subject: [PATCH 4/4] docs: tag shared Cursor README activation links --- third_party/qwen-image/README.md | 8 ++++---- third_party/qwen-image/readme/README_zh_Hans.md | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/third_party/qwen-image/README.md b/third_party/qwen-image/README.md index 8639cdcbc..264a56fe8 100644 --- a/third_party/qwen-image/README.md +++ b/third_party/qwen-image/README.md @@ -1,8 +1,8 @@ # Qwen Image for Cursor -Generate and edit Qwen Image pictures through [Ace Data Cloud](https://platform.acedata.cloud) in Cursor. This focused plugin reuses the existing hosted Qwen Image MCP for generation, editing, model listing, and same-task retrieval. The live MCP schemas define available actions and arguments. +Generate and edit Qwen Image pictures through [Ace Data Cloud](https://platform.acedata.cloud?utm_source=opensource&utm_medium=referral&utm_campaign=evergreen&utm_content=qwen_image_cursor_shared_readme_platform_en) in Cursor. This focused plugin reuses the existing hosted Qwen Image MCP for generation, editing, model listing, and same-task retrieval. The live MCP schemas define available actions and arguments. -[简体中文教程](readme/README_zh_Hans.md) · [Copyable first-run prompt](examples/first-image.md) · [Current pricing](https://platform.acedata.cloud/models) +[简体中文教程](readme/README_zh_Hans.md) · [Copyable first-run prompt](examples/first-image.md) · [Current pricing](https://platform.acedata.cloud/models?utm_source=opensource&utm_medium=referral&utm_campaign=evergreen&utm_content=qwen_image_cursor_shared_readme_pricing_en) ## 1. Install @@ -10,9 +10,9 @@ In Cursor 3.24, open **Customize → Browse Marketplace**, search for **Qwen Ima ## 2. Get the right API key -1. Sign in at [Ace Data Cloud Applications](https://platform.acedata.cloud/console/applications). +1. Sign in at [Ace Data Cloud Applications](https://platform.acedata.cloud/console/applications?utm_source=opensource&utm_medium=referral&utm_campaign=evergreen&utm_content=qwen_image_cursor_shared_readme_api_key_en). 2. Open **General application** or a Qwen Image-specific application. Copy an existing service API key, or use **Manage Keys → Create** for a separate Cursor key. -3. Check Qwen Image access, [current pricing](https://platform.acedata.cloud/models), and balance. If **Allowed APIs** restrictions are enabled, include both `/qwen-image/images` and `/qwen-image/tasks`. +3. Check Qwen Image access, [current pricing](https://platform.acedata.cloud/models?utm_source=opensource&utm_medium=referral&utm_campaign=evergreen&utm_content=qwen_image_cursor_shared_readme_pricing_en), and balance. If **Allowed APIs** restrictions are enabled, include both `/qwen-image/images` and `/qwen-image/tasks`. Use a service API key, not a platform management token. Do not put it in prompts or repository files. diff --git a/third_party/qwen-image/readme/README_zh_Hans.md b/third_party/qwen-image/readme/README_zh_Hans.md index 6e5132b4c..30ac59ccd 100644 --- a/third_party/qwen-image/readme/README_zh_Hans.md +++ b/third_party/qwen-image/readme/README_zh_Hans.md @@ -1,8 +1,8 @@ # Cursor Qwen Image 插件 -通过 [Ace Data Cloud](https://platform.acedata.cloud) 在 Cursor 中生成或编辑 Qwen Image 图片。插件复用现有托管 Qwen Image MCP,提供生成、编辑、模型列表及同任务查询工具;可用参数以在线工具 schema 为准。 +通过 [Ace Data Cloud](https://platform.acedata.cloud?utm_source=opensource&utm_medium=referral&utm_campaign=evergreen&utm_content=qwen_image_cursor_shared_readme_platform_zh_hans) 在 Cursor 中生成或编辑 Qwen Image 图片。插件复用现有托管 Qwen Image MCP,提供生成、编辑、模型列表及同任务查询工具;可用参数以在线工具 schema 为准。 -[English guide](../README.md) · [可复制的首跑提示词](../examples/first-image.md) · [当前价格](https://platform.acedata.cloud/models) +[English guide](../README.md) · [可复制的首跑提示词](../examples/first-image.md) · [当前价格](https://platform.acedata.cloud/models?utm_source=opensource&utm_medium=referral&utm_campaign=evergreen&utm_content=qwen_image_cursor_shared_readme_pricing_zh_hans) ## 1. 安装 @@ -10,9 +10,9 @@ ## 2. 获取服务 API Key -1. 登录 [Ace Data Cloud 应用管理](https://platform.acedata.cloud/console/applications)。 +1. 登录 [Ace Data Cloud 应用管理](https://platform.acedata.cloud/console/applications?utm_source=opensource&utm_medium=referral&utm_campaign=evergreen&utm_content=qwen_image_cursor_shared_readme_api_key_zh_hans)。 2. 打开 **General application** 或 Qwen Image 专用应用,复制已有服务 Key;也可通过 **Manage Keys → Create** 新建 Cursor 专用 Key。 -3. 确认 Qwen Image 权限、[当前价格](https://platform.acedata.cloud/models)和余额。若启用了 **Allowed APIs**,同时允许 `/qwen-image/images` 和 `/qwen-image/tasks`。 +3. 确认 Qwen Image 权限、[当前价格](https://platform.acedata.cloud/models?utm_source=opensource&utm_medium=referral&utm_campaign=evergreen&utm_content=qwen_image_cursor_shared_readme_pricing_zh_hans)和余额。若启用了 **Allowed APIs**,同时允许 `/qwen-image/images` 和 `/qwen-image/tasks`。 使用服务 API Key,不要使用平台管理 token;也不要把 Key 写进提示词或仓库。