instantclips-mcp 1.4.0 → 1.6.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.es.md +7 -2
- package/README.md +5 -0
- package/README.zh-CN.md +5 -2
- package/bin/instantclips-mcp.js +52 -6
- package/manifest/instantclips-mcp.json +2515 -10
- package/package.json +1 -1
package/README.es.md
CHANGED
|
@@ -94,7 +94,10 @@ Aplicación de Claude: añade un conector personalizado con esta dirección e in
|
|
|
94
94
|
lo pida. ChatGPT en la web: activa el modo de desarrollador en Ajustes, Apps, Avanzado y añade la
|
|
95
95
|
dirección como conector; en un espacio de trabajo Business o Enterprise, un administrador la
|
|
96
96
|
publica como aplicación para todo el equipo. La aplicación de escritorio de ChatGPT acepta la misma
|
|
97
|
-
dirección en Ajustes, Servidores MCP, y la comparte con Codex.
|
|
97
|
+
dirección en Ajustes, Servidores MCP, y la comparte con Codex. En ChatGPT, adjunta las fotos del
|
|
98
|
+
producto a la conversación y pide el vídeo: el plugin recibe los adjuntos directamente
|
|
99
|
+
(`image_files` en `create_product_from_images`, `add_image_files` en `update_product`), así que
|
|
100
|
+
allí las fotos no necesitan adaptador ni token.
|
|
98
101
|
|
|
99
102
|
### Cualquier otro cliente o agente MCP
|
|
100
103
|
|
|
@@ -168,7 +171,9 @@ acepta `image_paths` (rutas a los archivos, hasta 9, de 8 MB cada uno) en lugar
|
|
|
168
171
|
`update_product` acepta `add_image_paths`. Los archivos se empaquetan y se envían directamente a
|
|
169
172
|
InstantClips como fotos del producto; no se descarga nada de ningún sitio. Arrastra los archivos a
|
|
170
173
|
un cliente que le pase sus rutas al asistente (Claude Code, Cursor, VS Code, agentes de terminal) y
|
|
171
|
-
di qué quieres crear.
|
|
174
|
+
di qué quieres crear. Envía `image_paths` solo: si en la misma llamada van también `image_urls`, el
|
|
175
|
+
adaptador la rechaza en lugar de descartar las URL; añade las fotos alojadas después con
|
|
176
|
+
`update_product`.
|
|
172
177
|
|
|
173
178
|
## Instrucciones para empezar
|
|
174
179
|
|
package/README.md
CHANGED
|
@@ -88,6 +88,9 @@ Claude app: add a custom connector with this address and sign in when it asks. C
|
|
|
88
88
|
web: turn on Developer mode under Settings, Apps, Advanced, then add the address as a connector;
|
|
89
89
|
on a Business or Enterprise workspace an admin publishes it as an app for everyone instead. The
|
|
90
90
|
ChatGPT desktop app takes the same address under Settings, MCP servers, and shares it with Codex.
|
|
91
|
+
In ChatGPT, attach the product photos to the conversation and ask for the video: the plugin takes
|
|
92
|
+
attachments directly (`image_files` on `create_product_from_images`, `add_image_files` on
|
|
93
|
+
`update_product`), so photos need no adapter and no token there.
|
|
91
94
|
|
|
92
95
|
### Any other MCP client or agent
|
|
93
96
|
|
|
@@ -158,6 +161,8 @@ that never left your laptop. Through the adapter, `create_product_from_images` t
|
|
|
158
161
|
`add_image_paths`. The files are packaged and posted straight to InstantClips as the product's
|
|
159
162
|
photos — nothing is downloaded from anywhere. Drag the files into a client that hands the
|
|
160
163
|
assistant their paths (Claude Code, Cursor, VS Code, terminal agents) and say what you want made.
|
|
164
|
+
Send `image_paths` on its own: with hosted `image_urls` in the same call the adapter refuses
|
|
165
|
+
rather than dropping the URLs; add hosted photos afterwards with `update_product`.
|
|
161
166
|
|
|
162
167
|
## Starter prompts
|
|
163
168
|
|
package/README.zh-CN.md
CHANGED
|
@@ -78,7 +78,9 @@ url = "https://app.instantclips.ai/mcp"
|
|
|
78
78
|
|
|
79
79
|
Claude 应用:用这个地址添加自定义连接器,按提示登录。网页版 ChatGPT:在 设置 › Apps › 高级 中
|
|
80
80
|
开启开发者模式,再把地址添加为连接器;Business 或 Enterprise 工作区则由管理员发布为全员可用的
|
|
81
|
-
应用。ChatGPT 桌面版在 设置 › MCP 服务器 中填入同一地址,并与 Codex
|
|
81
|
+
应用。ChatGPT 桌面版在 设置 › MCP 服务器 中填入同一地址,并与 Codex 共享配置。在 ChatGPT 里,把商品照片
|
|
82
|
+
作为附件添加到对话中并提出需求即可:插件会直接接收附件(`create_product_from_images` 的 `image_files`、
|
|
83
|
+
`update_product` 的 `add_image_files`),因此在那里传照片不需要适配器,也不需要令牌。
|
|
82
84
|
|
|
83
85
|
### 其他 MCP 客户端或智能体
|
|
84
86
|
|
|
@@ -143,7 +145,8 @@ INSTANTCLIPS_TOKEN="your-token" npx -y instantclips-mcp --check --json
|
|
|
143
145
|
`create_product_from_images` 可用 `image_paths`(文件路径,最多 9 个,每个不超过 8 MB)代替
|
|
144
146
|
`image_urls`,`update_product` 可用 `add_image_paths`。文件会被打包并直接发送到 InstantClips 作为商品照片,
|
|
145
147
|
不会从任何地方下载。把文件拖进会把路径交给助手的客户端(Claude Code、Cursor、VS Code、终端智能体),
|
|
146
|
-
|
|
148
|
+
然后说出你想要的视频即可。`image_paths` 请单独发送:同一次调用里若还带有 `image_urls`,适配器会拒绝,
|
|
149
|
+
而不是悄悄丢弃这些 URL;托管照片可以之后用 `update_product` 添加。
|
|
147
150
|
|
|
148
151
|
## 入门提示语
|
|
149
152
|
|
package/bin/instantclips-mcp.js
CHANGED
|
@@ -141,6 +141,14 @@ function writeDiagnostic(failure) {
|
|
|
141
141
|
// serves. The files are read and posted as multipart to the hosted upload
|
|
142
142
|
// endpoints under the same bearer — packaged and uploaded, never downloaded
|
|
143
143
|
// from anywhere.
|
|
144
|
+
//
|
|
145
|
+
// The hosted server also takes the conversation's attachments — `image_files`
|
|
146
|
+
// and `add_image_files`, declared for ChatGPT through `_meta["openai/fileParams"]`
|
|
147
|
+
// — which a stdio client has no way to fill. Those two parameters and the
|
|
148
|
+
// `_meta` are removed from the schemas served here, so the model sees one
|
|
149
|
+
// local-file parameter and not a hosted one it cannot use. A call that sends
|
|
150
|
+
// `image_paths` together with hosted URLs is refused rather than uploading
|
|
151
|
+
// the files and dropping the URLs, which is what 1.4.0 did.
|
|
144
152
|
const MAX_LOCAL_IMAGES = 9;
|
|
145
153
|
const MAX_LOCAL_IMAGE_BYTES = 8 * 1024 * 1024;
|
|
146
154
|
const LOCAL_IMAGE_TYPES = {
|
|
@@ -159,14 +167,17 @@ const LOCAL_UPLOAD_PARAMETERS = {
|
|
|
159
167
|
create_product_from_images: {
|
|
160
168
|
name: "image_paths",
|
|
161
169
|
replaces: "image_urls",
|
|
170
|
+
hides: "image_files",
|
|
162
171
|
description:
|
|
163
172
|
`Paths to photos on this machine, up to ${MAX_LOCAL_IMAGES}, ${MAX_LOCAL_IMAGE_BYTES / 1024 / 1024} MB each. ` +
|
|
164
|
-
"This adapter uploads the files itself; use it instead of image_urls for local photos.",
|
|
173
|
+
"This adapter uploads the files itself; use it instead of image_urls for local photos, never with them.",
|
|
165
174
|
note:
|
|
166
|
-
"Through this adapter, `image_paths` (files on this machine)
|
|
175
|
+
"Through this adapter, `image_paths` (files on this machine) replaces `image_urls`; the files are uploaded directly. " +
|
|
176
|
+
"Send one or the other, not both.",
|
|
167
177
|
},
|
|
168
178
|
update_product: {
|
|
169
179
|
name: "add_image_paths",
|
|
180
|
+
hides: "add_image_files",
|
|
170
181
|
description:
|
|
171
182
|
`Paths to photos on this machine to add, up to ${MAX_LOCAL_IMAGES}, ${MAX_LOCAL_IMAGE_BYTES / 1024 / 1024} MB each. ` +
|
|
172
183
|
"This adapter uploads the files itself. Send it with product_id alone; other fields go in a separate call.",
|
|
@@ -180,18 +191,35 @@ function withLocalUploads(tools) {
|
|
|
180
191
|
const extra = LOCAL_UPLOAD_PARAMETERS[tool.name];
|
|
181
192
|
if (!extra) return tool;
|
|
182
193
|
const schema = tool.inputSchema || { type: "object", properties: {} };
|
|
194
|
+
const { [extra.hides]: _hidden, ...kept } = schema.properties || {};
|
|
183
195
|
const properties = {
|
|
184
|
-
...
|
|
185
|
-
[extra.name]: { type: "array", items: { type: "string" }, description: extra.description },
|
|
196
|
+
...kept,
|
|
197
|
+
[extra.name]: { type: "array", items: { type: "string" }, minItems: 1, description: extra.description },
|
|
186
198
|
};
|
|
187
199
|
const inputSchema = { ...schema, properties };
|
|
188
200
|
if (extra.replaces && Array.isArray(schema.required)) {
|
|
189
201
|
inputSchema.required = schema.required.filter((key) => key !== extra.replaces);
|
|
190
202
|
}
|
|
191
|
-
|
|
203
|
+
const { _meta, ...rest } = tool;
|
|
204
|
+
return {
|
|
205
|
+
...rest,
|
|
206
|
+
...withoutFileParam(_meta, extra.hides),
|
|
207
|
+
description: `${tool.description}\n\n${extra.note}`,
|
|
208
|
+
inputSchema,
|
|
209
|
+
};
|
|
192
210
|
});
|
|
193
211
|
}
|
|
194
212
|
|
|
213
|
+
// → `{ _meta }` with the hidden file parameter's declaration removed, or `{}`
|
|
214
|
+
// when nothing else was in it.
|
|
215
|
+
function withoutFileParam(meta, hidden) {
|
|
216
|
+
if (!meta) return {};
|
|
217
|
+
const { "openai/fileParams": fileParams, ...others } = meta;
|
|
218
|
+
const remaining = (fileParams || []).filter((name) => name !== hidden);
|
|
219
|
+
const kept = remaining.length ? { ...others, "openai/fileParams": remaining } : others;
|
|
220
|
+
return Object.keys(kept).length ? { _meta: kept } : {};
|
|
221
|
+
}
|
|
222
|
+
|
|
195
223
|
function hasPaths(value) {
|
|
196
224
|
return Array.isArray(value) && value.length > 0;
|
|
197
225
|
}
|
|
@@ -250,13 +278,31 @@ async function uploadForm(config, suffix, form) {
|
|
|
250
278
|
error.data = { status: 401 };
|
|
251
279
|
throw error;
|
|
252
280
|
}
|
|
253
|
-
return { content: [{ type: "text", text }], isError:
|
|
281
|
+
if (!response.ok) return { content: [{ type: "text", text }], isError: true };
|
|
282
|
+
// The hosted tools declare output schemas, and a validating client refuses
|
|
283
|
+
// a result that has one but no structuredContent — so the upload's payload
|
|
284
|
+
// (the same product object get_product returns) travels both ways.
|
|
285
|
+
const result = { content: [{ type: "text", text }], isError: false };
|
|
286
|
+
try {
|
|
287
|
+
const parsed = JSON.parse(text);
|
|
288
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) result.structuredContent = parsed;
|
|
289
|
+
} catch {
|
|
290
|
+
// Not JSON: the text block stands alone, as it always did.
|
|
291
|
+
}
|
|
292
|
+
return result;
|
|
254
293
|
}
|
|
255
294
|
|
|
256
295
|
// → a tool result when the call is one this adapter answers itself, else null.
|
|
257
296
|
async function callLocally(config, params) {
|
|
258
297
|
const args = params.arguments ?? {};
|
|
259
298
|
if (params.name === "create_product_from_images" && hasPaths(args.image_paths)) {
|
|
299
|
+
const hosted = ["image_urls", "image_files"].filter((key) => Array.isArray(args[key]) && args[key].length > 0);
|
|
300
|
+
if (hosted.length > 0) {
|
|
301
|
+
throw uploadError(
|
|
302
|
+
`Send image_paths alone: ${hosted.join(", ")} would be dropped. Create the product from image_paths, ` +
|
|
303
|
+
"then add hosted photos with update_product's add_image_urls.",
|
|
304
|
+
);
|
|
305
|
+
}
|
|
260
306
|
const form = new FormData();
|
|
261
307
|
for (const key of ["name", "description", "creator_note", "brand_id"]) {
|
|
262
308
|
if (args[key] !== undefined && args[key] !== null && args[key] !== "") form.append(key, String(args[key]));
|