@productmaker/mcp 3.1.0 → 3.1.2
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 +45 -21
- package/dist/http.js +2956 -1527
- package/dist/stdio.js +222 -58
- package/dist/ui/meta-upload-app.html +135 -40
- package/package.json +4 -2
package/dist/stdio.js
CHANGED
|
@@ -5,11 +5,88 @@ var __name = (target, value) => __defProp(target, "name", { value, configurable:
|
|
|
5
5
|
// src/stdio.ts
|
|
6
6
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
7
7
|
|
|
8
|
+
// src/auth/downstream-credential.ts
|
|
9
|
+
import { createHash } from "crypto";
|
|
10
|
+
var StaticApiKeyCredential = class {
|
|
11
|
+
static {
|
|
12
|
+
__name(this, "StaticApiKeyCredential");
|
|
13
|
+
}
|
|
14
|
+
apiKey;
|
|
15
|
+
constructor(apiKey) {
|
|
16
|
+
this.apiKey = apiKey;
|
|
17
|
+
}
|
|
18
|
+
async getAccessToken() {
|
|
19
|
+
return this.apiKey;
|
|
20
|
+
}
|
|
21
|
+
fingerprint() {
|
|
22
|
+
return createHash("sha256").update(this.apiKey).digest("hex").slice(0, 32);
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
|
|
8
26
|
// src/server.ts
|
|
9
27
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
10
28
|
|
|
29
|
+
// src/response-body.ts
|
|
30
|
+
var ResponseBodyTooLargeError = class extends Error {
|
|
31
|
+
static {
|
|
32
|
+
__name(this, "ResponseBodyTooLargeError");
|
|
33
|
+
}
|
|
34
|
+
maxBytes;
|
|
35
|
+
constructor(maxBytes) {
|
|
36
|
+
super(`Response body exceeds the ${maxBytes}-byte limit`), this.maxBytes = maxBytes;
|
|
37
|
+
this.name = "ResponseBodyTooLargeError";
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
function cancelResponseBody(response) {
|
|
41
|
+
if (!response.body) return;
|
|
42
|
+
void response.body.cancel().catch(() => void 0);
|
|
43
|
+
}
|
|
44
|
+
__name(cancelResponseBody, "cancelResponseBody");
|
|
45
|
+
async function readResponseBytes(response, maxBytes) {
|
|
46
|
+
if (!Number.isSafeInteger(maxBytes) || maxBytes <= 0) {
|
|
47
|
+
throw new RangeError("maxBytes must be a positive safe integer");
|
|
48
|
+
}
|
|
49
|
+
if (!response.body) return new Uint8Array();
|
|
50
|
+
const contentLength = response.headers.get("content-length");
|
|
51
|
+
const declaredLength = contentLength === null ? NaN : Number(contentLength);
|
|
52
|
+
if (Number.isSafeInteger(declaredLength) && declaredLength > maxBytes) {
|
|
53
|
+
cancelResponseBody(response);
|
|
54
|
+
throw new ResponseBodyTooLargeError(maxBytes);
|
|
55
|
+
}
|
|
56
|
+
const reader = response.body.getReader();
|
|
57
|
+
const chunks = [];
|
|
58
|
+
let totalBytes = 0;
|
|
59
|
+
try {
|
|
60
|
+
while (true) {
|
|
61
|
+
const { done, value } = await reader.read();
|
|
62
|
+
if (done) break;
|
|
63
|
+
totalBytes += value.byteLength;
|
|
64
|
+
if (totalBytes > maxBytes) {
|
|
65
|
+
void reader.cancel().catch(() => void 0);
|
|
66
|
+
throw new ResponseBodyTooLargeError(maxBytes);
|
|
67
|
+
}
|
|
68
|
+
chunks.push(value);
|
|
69
|
+
}
|
|
70
|
+
} finally {
|
|
71
|
+
reader.releaseLock();
|
|
72
|
+
}
|
|
73
|
+
const body = new Uint8Array(totalBytes);
|
|
74
|
+
let offset = 0;
|
|
75
|
+
for (const chunk of chunks) {
|
|
76
|
+
body.set(chunk, offset);
|
|
77
|
+
offset += chunk.byteLength;
|
|
78
|
+
}
|
|
79
|
+
return body;
|
|
80
|
+
}
|
|
81
|
+
__name(readResponseBytes, "readResponseBytes");
|
|
82
|
+
async function readResponseText(response, maxBytes) {
|
|
83
|
+
return new TextDecoder().decode(await readResponseBytes(response, maxBytes));
|
|
84
|
+
}
|
|
85
|
+
__name(readResponseText, "readResponseText");
|
|
86
|
+
|
|
11
87
|
// src/api-client.ts
|
|
12
|
-
|
|
88
|
+
var DOWNSTREAM_REQUEST_TIMEOUT_MS = 3e4;
|
|
89
|
+
var MAX_DOWNSTREAM_RESPONSE_BYTES = 1048576;
|
|
13
90
|
var ApiHttpError = class extends Error {
|
|
14
91
|
static {
|
|
15
92
|
__name(this, "ApiHttpError");
|
|
@@ -25,88 +102,95 @@ var ApiClient = class {
|
|
|
25
102
|
static {
|
|
26
103
|
__name(this, "ApiClient");
|
|
27
104
|
}
|
|
28
|
-
apiKey;
|
|
29
105
|
baseUrl;
|
|
30
|
-
|
|
31
|
-
|
|
106
|
+
credential;
|
|
107
|
+
signal;
|
|
108
|
+
requestTimeoutMs;
|
|
109
|
+
constructor(baseUrl, credential, options = {}) {
|
|
32
110
|
this.baseUrl = baseUrl.replace(/\/+$/, "");
|
|
111
|
+
this.credential = credential;
|
|
112
|
+
this.signal = options.signal;
|
|
113
|
+
this.requestTimeoutMs = options.requestTimeoutMs ?? DOWNSTREAM_REQUEST_TIMEOUT_MS;
|
|
33
114
|
}
|
|
34
|
-
authHeaders() {
|
|
115
|
+
async authHeaders() {
|
|
35
116
|
return {
|
|
36
|
-
Authorization: `Bearer ${this.
|
|
117
|
+
Authorization: `Bearer ${await this.credential.getAccessToken()}`
|
|
37
118
|
};
|
|
38
119
|
}
|
|
39
120
|
/**
|
|
40
|
-
* Stable per-
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
121
|
+
* Stable per-credential identifier. The MCP never sees the userId, so this
|
|
122
|
+
* stands in as the user component of derived idempotency keys. Never log or
|
|
123
|
+
* return the raw credential material to a caller: implementations hash or
|
|
124
|
+
* otherwise derive a safe fingerprint.
|
|
44
125
|
*/
|
|
45
126
|
fingerprint() {
|
|
46
|
-
return
|
|
127
|
+
return this.credential.fingerprint();
|
|
47
128
|
}
|
|
48
129
|
publicUrl(path3) {
|
|
49
130
|
const normalizedPath = path3.startsWith("/") ? path3 : `/${path3}`;
|
|
50
131
|
return new URL(`${this.baseUrl}${normalizedPath}`).toString();
|
|
51
132
|
}
|
|
52
|
-
async getJson(path3) {
|
|
53
|
-
const res = await
|
|
54
|
-
headers: this.authHeaders()
|
|
55
|
-
});
|
|
133
|
+
async getJson(path3, options) {
|
|
134
|
+
const res = await this.request(`${this.baseUrl}${path3}`, {
|
|
135
|
+
headers: await this.authHeaders()
|
|
136
|
+
}, options);
|
|
56
137
|
return this.parse(res);
|
|
57
138
|
}
|
|
58
|
-
async postJson(path3, body) {
|
|
59
|
-
const
|
|
139
|
+
async postJson(path3, body, options) {
|
|
140
|
+
const authHeaders = await this.authHeaders();
|
|
141
|
+
const res = await this.request(`${this.baseUrl}${path3}`, {
|
|
60
142
|
method: "POST",
|
|
61
143
|
headers: {
|
|
62
|
-
...
|
|
144
|
+
...authHeaders,
|
|
63
145
|
"Content-Type": "application/json"
|
|
64
146
|
},
|
|
65
147
|
body: JSON.stringify(body)
|
|
66
|
-
});
|
|
148
|
+
}, options);
|
|
67
149
|
return this.parse(res);
|
|
68
150
|
}
|
|
69
|
-
async patchJson(path3, body) {
|
|
70
|
-
const
|
|
151
|
+
async patchJson(path3, body, options) {
|
|
152
|
+
const authHeaders = await this.authHeaders();
|
|
153
|
+
const res = await this.request(`${this.baseUrl}${path3}`, {
|
|
71
154
|
method: "PATCH",
|
|
72
155
|
headers: {
|
|
73
|
-
...
|
|
156
|
+
...authHeaders,
|
|
74
157
|
"Content-Type": "application/json"
|
|
75
158
|
},
|
|
76
159
|
body: JSON.stringify(body)
|
|
77
|
-
});
|
|
160
|
+
}, options);
|
|
78
161
|
return this.parse(res);
|
|
79
162
|
}
|
|
80
|
-
async putJson(path3, body) {
|
|
81
|
-
const
|
|
163
|
+
async putJson(path3, body, options) {
|
|
164
|
+
const authHeaders = await this.authHeaders();
|
|
165
|
+
const res = await this.request(`${this.baseUrl}${path3}`, {
|
|
82
166
|
method: "PUT",
|
|
83
167
|
headers: {
|
|
84
|
-
...
|
|
168
|
+
...authHeaders,
|
|
85
169
|
"Content-Type": "application/json"
|
|
86
170
|
},
|
|
87
171
|
body: JSON.stringify(body)
|
|
88
|
-
});
|
|
172
|
+
}, options);
|
|
89
173
|
return this.parse(res);
|
|
90
174
|
}
|
|
91
|
-
async deleteJson(path3) {
|
|
92
|
-
const res = await
|
|
175
|
+
async deleteJson(path3, options) {
|
|
176
|
+
const res = await this.request(`${this.baseUrl}${path3}`, {
|
|
93
177
|
method: "DELETE",
|
|
94
|
-
headers: this.authHeaders()
|
|
95
|
-
});
|
|
178
|
+
headers: await this.authHeaders()
|
|
179
|
+
}, options);
|
|
96
180
|
return this.parse(res);
|
|
97
181
|
}
|
|
98
182
|
/** Upload bytes to a presigned URL; the URL itself does not need API auth. */
|
|
99
|
-
async putBytes(url, body, contentType) {
|
|
100
|
-
const res = await
|
|
183
|
+
async putBytes(url, body, contentType, options) {
|
|
184
|
+
const res = await this.request(url, {
|
|
101
185
|
method: "PUT",
|
|
102
186
|
headers: {
|
|
103
187
|
"Content-Type": contentType,
|
|
104
188
|
"Content-Length": String(body.byteLength)
|
|
105
189
|
},
|
|
106
190
|
body: new Uint8Array(body)
|
|
107
|
-
});
|
|
191
|
+
}, options);
|
|
108
192
|
if (!res.ok) {
|
|
109
|
-
throw new ApiHttpError(res.status, await res
|
|
193
|
+
throw new ApiHttpError(res.status, await readResponseText(res, MAX_DOWNSTREAM_RESPONSE_BYTES).catch(() => ""), `HTTP ${res.status} uploading media`);
|
|
110
194
|
}
|
|
111
195
|
}
|
|
112
196
|
async postMultipart(path3, files, fields, options) {
|
|
@@ -131,34 +215,56 @@ var ApiClient = class {
|
|
|
131
215
|
form.append(k, v);
|
|
132
216
|
}
|
|
133
217
|
}
|
|
134
|
-
const
|
|
218
|
+
const authHeaders = await this.authHeaders();
|
|
219
|
+
const res = await this.request(`${this.baseUrl}${path3}`, {
|
|
135
220
|
method: "POST",
|
|
136
221
|
headers: {
|
|
137
|
-
...
|
|
222
|
+
...authHeaders,
|
|
138
223
|
...options?.idempotencyKey ? {
|
|
139
224
|
"Idempotency-Key": options.idempotencyKey
|
|
140
225
|
} : {}
|
|
141
226
|
},
|
|
142
227
|
body: form
|
|
143
|
-
});
|
|
228
|
+
}, options);
|
|
144
229
|
return this.parse(res);
|
|
145
230
|
}
|
|
231
|
+
request(input, init, options) {
|
|
232
|
+
const signals = [
|
|
233
|
+
AbortSignal.timeout(this.requestTimeoutMs),
|
|
234
|
+
this.signal,
|
|
235
|
+
options?.signal
|
|
236
|
+
].filter((signal2) => signal2 !== void 0);
|
|
237
|
+
const signal = signals.length === 1 ? signals[0] : AbortSignal.any(signals);
|
|
238
|
+
return fetch(input, {
|
|
239
|
+
...init,
|
|
240
|
+
signal
|
|
241
|
+
});
|
|
242
|
+
}
|
|
146
243
|
async parse(res) {
|
|
244
|
+
let bodyText;
|
|
245
|
+
try {
|
|
246
|
+
bodyText = await readResponseText(res, MAX_DOWNSTREAM_RESPONSE_BYTES);
|
|
247
|
+
} catch (error) {
|
|
248
|
+
if (!res.ok) {
|
|
249
|
+
throw new ApiHttpError(res.status, "[response body unavailable]", `HTTP ${res.status} from ${res.url}`);
|
|
250
|
+
}
|
|
251
|
+
throw error;
|
|
252
|
+
}
|
|
147
253
|
if (!res.ok) {
|
|
148
254
|
let body;
|
|
149
255
|
try {
|
|
150
|
-
body =
|
|
256
|
+
body = JSON.parse(bodyText);
|
|
151
257
|
} catch {
|
|
152
|
-
body =
|
|
258
|
+
body = bodyText;
|
|
153
259
|
}
|
|
154
260
|
throw new ApiHttpError(res.status, body, `HTTP ${res.status} from ${res.url}`);
|
|
155
261
|
}
|
|
156
|
-
return
|
|
262
|
+
return JSON.parse(bodyText);
|
|
157
263
|
}
|
|
158
264
|
};
|
|
159
265
|
|
|
160
266
|
// src/index.ts
|
|
161
|
-
var MCP_VERSION = "3.1.
|
|
267
|
+
var MCP_VERSION = "3.1.2";
|
|
162
268
|
|
|
163
269
|
// src/meta-upload-app.ts
|
|
164
270
|
import { readFileSync } from "fs";
|
|
@@ -425,8 +531,15 @@ async function resolveImageUrl(rawUrl) {
|
|
|
425
531
|
}
|
|
426
532
|
const ct = (res.headers.get("content-type") ?? "").split(";")[0]?.trim() ?? "";
|
|
427
533
|
if (!ALLOWED_MIMES.has(ct)) throw new ResolveImageError("IMAGE_TYPE_UNSUPPORTED", `content-type ${ct}`);
|
|
428
|
-
|
|
429
|
-
|
|
534
|
+
let buf;
|
|
535
|
+
try {
|
|
536
|
+
buf = Buffer.from(await readResponseBytes(res, MAX_BYTES));
|
|
537
|
+
} catch (error) {
|
|
538
|
+
if (error instanceof ResponseBodyTooLargeError) {
|
|
539
|
+
throw new ResolveImageError("IMAGE_TOO_LARGE", `image URL exceeds ${MAX_BYTES} bytes`);
|
|
540
|
+
}
|
|
541
|
+
throw new ResolveImageError("IMAGE_DOWNLOAD_FAILED", "image response could not be read");
|
|
542
|
+
}
|
|
430
543
|
const sniffed = sniffMime(buf);
|
|
431
544
|
if (!sniffed || sniffed !== ct) {
|
|
432
545
|
throw new ResolveImageError("IMAGE_TYPE_UNSUPPORTED", `magic bytes (${sniffed ?? "unknown"}) do not match content-type (${ct})`);
|
|
@@ -6871,6 +6984,16 @@ var DEFAULT_PRICING = {
|
|
|
6871
6984
|
ourCostCents: 0.15,
|
|
6872
6985
|
userPriceCents: 0.3
|
|
6873
6986
|
},
|
|
6987
|
+
// ⚠️ Both call sites below moved from gemini-3-flash-preview to gemini-3.7-flash on
|
|
6988
|
+
// 2026-08-15 (#1418) and these rates predate that. They are NOT re-measured, and the
|
|
6989
|
+
// numbers are LEFT UNCHANGED by the same owner decision that governs the whole table
|
|
6990
|
+
// (launch pricing is expected to rise; real user billing derives from actual
|
|
6991
|
+
// usage.cost, so these only feed the estimator and the pre-flight quote ceiling).
|
|
6992
|
+
// - product-accuracy: ZERO calls in the last 20 days of prod billing, so there is no
|
|
6993
|
+
// measurement to compare against and the gate may not be reachable at all.
|
|
6994
|
+
// - wearable-product-semantics: measured 0.4303¢/call over 121 prod calls (4,970
|
|
6995
|
+
// prompt tokens) on flash-preview — this 0.11¢ fallback already UNDER-quotes by
|
|
6996
|
+
// ~4x, independently of the model move. Re-measure after #1418 is deployed.
|
|
6874
6997
|
"openrouter:product-accuracy": {
|
|
6875
6998
|
ourCostCents: 0.15,
|
|
6876
6999
|
userPriceCents: 0.3
|
|
@@ -9182,13 +9305,21 @@ var WHATSAPP_ONLY_CHANNEL_EXPOSURE = {
|
|
|
9182
9305
|
mcp: "unavailable",
|
|
9183
9306
|
whatsapp: "adapted"
|
|
9184
9307
|
};
|
|
9185
|
-
function defineCapability(name, schemaHash, confirmation, routingDomain, effects, inputSlots, consumes, produces, channelExposure = SHARED_CHANNEL_EXPOSURE) {
|
|
9308
|
+
function defineCapability(name, schemaHash, confirmation, routingDomain, effects, inputSlots, consumes, produces, channelExposure = SHARED_CHANNEL_EXPOSURE, conversationInputs) {
|
|
9186
9309
|
return {
|
|
9187
9310
|
name,
|
|
9188
9311
|
schemaHash,
|
|
9189
9312
|
confirmation,
|
|
9190
9313
|
routingDomain,
|
|
9191
9314
|
inputSlots,
|
|
9315
|
+
conversation: {
|
|
9316
|
+
inputs: conversationInputs ?? inputSlots.map((input) => ({
|
|
9317
|
+
slot: input.name,
|
|
9318
|
+
askBeforePaidExecution: input.required,
|
|
9319
|
+
explicitOmissionAllowed: !input.required,
|
|
9320
|
+
fieldCompletion: "any"
|
|
9321
|
+
}))
|
|
9322
|
+
},
|
|
9192
9323
|
effects,
|
|
9193
9324
|
artifacts: {
|
|
9194
9325
|
consumes,
|
|
@@ -9686,6 +9817,11 @@ var PRODUCTMAKER_CAPABILITY_REGISTRY = deepFreeze([
|
|
|
9686
9817
|
"compareAtPrice",
|
|
9687
9818
|
"offer",
|
|
9688
9819
|
"paymentMethods"
|
|
9820
|
+
], [
|
|
9821
|
+
"price",
|
|
9822
|
+
"offer",
|
|
9823
|
+
"compareAtPrice",
|
|
9824
|
+
"paymentMethods"
|
|
9689
9825
|
]),
|
|
9690
9826
|
slot("product_context", false, [
|
|
9691
9827
|
"productContext",
|
|
@@ -9707,6 +9843,25 @@ var PRODUCTMAKER_CAPABILITY_REGISTRY = deepFreeze([
|
|
|
9707
9843
|
"product_reference"
|
|
9708
9844
|
], [
|
|
9709
9845
|
"landing"
|
|
9846
|
+
], SHARED_CHANNEL_EXPOSURE, [
|
|
9847
|
+
{
|
|
9848
|
+
slot: "product_reference",
|
|
9849
|
+
askBeforePaidExecution: true,
|
|
9850
|
+
explicitOmissionAllowed: false,
|
|
9851
|
+
fieldCompletion: "any"
|
|
9852
|
+
},
|
|
9853
|
+
{
|
|
9854
|
+
slot: "locale",
|
|
9855
|
+
askBeforePaidExecution: true,
|
|
9856
|
+
explicitOmissionAllowed: false,
|
|
9857
|
+
fieldCompletion: "all"
|
|
9858
|
+
},
|
|
9859
|
+
{
|
|
9860
|
+
slot: "commercial_offer",
|
|
9861
|
+
askBeforePaidExecution: true,
|
|
9862
|
+
explicitOmissionAllowed: true,
|
|
9863
|
+
fieldCompletion: "all"
|
|
9864
|
+
}
|
|
9710
9865
|
]),
|
|
9711
9866
|
defineCapability("generate_video_creative", "6bcd073df66023f8108a671b3027e1f35e9596cf9481ddf13a0282b4c56f5f34", "credit_consuming", "creative", [
|
|
9712
9867
|
"credit_consuming",
|
|
@@ -10081,7 +10236,7 @@ var PRODUCTMAKER_CAPABILITY_REGISTRY = deepFreeze([
|
|
|
10081
10236
|
], [
|
|
10082
10237
|
"meta_upload_session"
|
|
10083
10238
|
], WHATSAPP_ONLY_CHANNEL_EXPOSURE),
|
|
10084
|
-
defineCapability("request_media_upload", "
|
|
10239
|
+
defineCapability("request_media_upload", "e91cda1b2d9e44cecf28990b8018ce535ae4e9e6594fa80f503a1bf69d7146ee", "none", "creative", [
|
|
10085
10240
|
"state_change"
|
|
10086
10241
|
], [
|
|
10087
10242
|
slot("media_kind", true, [
|
|
@@ -10389,7 +10544,7 @@ async function readResponseWithCap(response) {
|
|
|
10389
10544
|
if (next.done) break;
|
|
10390
10545
|
total += next.value.byteLength;
|
|
10391
10546
|
if (total > MAX_MEDIA_BYTES) {
|
|
10392
|
-
|
|
10547
|
+
void reader.cancel().catch(() => void 0);
|
|
10393
10548
|
throw new ResolveMediaError("MEDIA_TOO_LARGE", `media URL exceeds ${MAX_MEDIA_BYTES} bytes`);
|
|
10394
10549
|
}
|
|
10395
10550
|
chunks.push(Buffer.from(next.value));
|
|
@@ -10537,6 +10692,7 @@ async function resolveMediaRef(ref, opts) {
|
|
|
10537
10692
|
if (!response.ok) throw new ResolveMediaError("MEDIA_URL_UNAVAILABLE", `media URL returned HTTP ${response.status}`);
|
|
10538
10693
|
const contentLength = Number(response.headers.get("content-length") ?? 0);
|
|
10539
10694
|
if (contentLength > MAX_MEDIA_BYTES) {
|
|
10695
|
+
void response.body?.cancel().catch(() => void 0);
|
|
10540
10696
|
throw new ResolveMediaError("MEDIA_TOO_LARGE", `media URL exceeds ${MAX_MEDIA_BYTES} bytes`);
|
|
10541
10697
|
}
|
|
10542
10698
|
const buffer = await readResponseWithCap(response);
|
|
@@ -11450,8 +11606,8 @@ function sanitizeFilename(s) {
|
|
|
11450
11606
|
return s.replace(/[^a-zA-Z0-9._-]+/g, "_").slice(0, 64) || "asset";
|
|
11451
11607
|
}
|
|
11452
11608
|
__name(sanitizeFilename, "sanitizeFilename");
|
|
11453
|
-
async function fetchTaskOutputs(api, taskId) {
|
|
11454
|
-
const raw = await api.getJson(`/v1/results/${encodeURIComponent(taskId)}?signedUrls=true
|
|
11609
|
+
async function fetchTaskOutputs(api, taskId, options) {
|
|
11610
|
+
const raw = await api.getJson(`/v1/results/${encodeURIComponent(taskId)}?signedUrls=true`, options);
|
|
11455
11611
|
const videos = (raw.videoGenerators ?? []).map((vg) => {
|
|
11456
11612
|
const variants2 = (vg.variants ?? []).filter((v) => Boolean(v.kind) && Boolean(v.url)).map((v) => ({
|
|
11457
11613
|
kind: v.kind,
|
|
@@ -11952,12 +12108,16 @@ function registerTaskTools(server, api, { proxyBaseUrl = null, transport = "http
|
|
|
11952
12108
|
});
|
|
11953
12109
|
server.tool("get_task_status", "Devuelve el estado y URLs de outputs de una tarea. Si waitSeconds>0, hace polling hasta que cambie la fase, aparezca `pendingUserAction`, o llegue al timeout (max 120s). Cuando la tarea queda en `done`, incluye enlaces a sus outputs.\n\nSi aparece `pendingUserAction`, el pipeline est\xE1 PARADO: muestra el draft y llama `resolve_pending_action` repitiendo el mismo `kind`. Kinds: `product_reference_set_approval`, `product_reference_confirmation`, `angle_review_approval`, `service_angle_approval` y `service_script_approval`.", GetTaskStatusInput.shape, async (input, extra) => {
|
|
11954
12110
|
try {
|
|
11955
|
-
const fetcher = /* @__PURE__ */ __name(() => api.getJson(`/v1/status/${encodeURIComponent(input.taskId)}`), "fetcher");
|
|
11956
12111
|
const signal = extra?.signal;
|
|
12112
|
+
const fetcher = /* @__PURE__ */ __name(() => api.getJson(`/v1/status/${encodeURIComponent(input.taskId)}`, {
|
|
12113
|
+
signal
|
|
12114
|
+
}), "fetcher");
|
|
11957
12115
|
const snapshot = await pollStatus(fetcher, input.waitSeconds ?? 0, signal);
|
|
11958
12116
|
if (snapshot.status !== "done") return asText(snapshot);
|
|
11959
12117
|
if (snapshot.kind === "service") {
|
|
11960
|
-
const result = await api.getJson(`/v1/tasks/${encodeURIComponent(input.taskId)}/service-result
|
|
12118
|
+
const result = await api.getJson(`/v1/tasks/${encodeURIComponent(input.taskId)}/service-result`, {
|
|
12119
|
+
signal
|
|
12120
|
+
});
|
|
11961
12121
|
return {
|
|
11962
12122
|
content: [
|
|
11963
12123
|
{
|
|
@@ -11977,7 +12137,9 @@ function registerTaskTools(server, api, { proxyBaseUrl = null, transport = "http
|
|
|
11977
12137
|
};
|
|
11978
12138
|
}
|
|
11979
12139
|
try {
|
|
11980
|
-
const rawOutputs = await fetchTaskOutputs(api, input.taskId
|
|
12140
|
+
const rawOutputs = await fetchTaskOutputs(api, input.taskId, {
|
|
12141
|
+
signal
|
|
12142
|
+
});
|
|
11981
12143
|
const outputs = proxifyOutputs(rawOutputs, proxyBaseUrl);
|
|
11982
12144
|
return {
|
|
11983
12145
|
content: [
|
|
@@ -13513,11 +13675,11 @@ var UploadSessionInputBase = z55.object({
|
|
|
13513
13675
|
maxFiles: z55.number().int().min(1).max(10).optional().describe("Optional batch limit: image accepts 1-5, video exactly 1, and mixed 1-10.")
|
|
13514
13676
|
});
|
|
13515
13677
|
var OpenAiFileReference = z55.object({
|
|
13516
|
-
download_url: z55.string().url(),
|
|
13678
|
+
download_url: z55.string().url().optional().describe("Optional host-provided download URL. The App prefers getFileDownloadUrl(file_id) and the server never fetches this URL."),
|
|
13517
13679
|
file_id: z55.string().min(1),
|
|
13518
13680
|
mime_type: z55.string().min(1).optional(),
|
|
13519
13681
|
file_name: z55.string().min(1).optional()
|
|
13520
|
-
})
|
|
13682
|
+
});
|
|
13521
13683
|
var UploadSessionToolInputShape = {
|
|
13522
13684
|
...UploadSessionInputBase.shape,
|
|
13523
13685
|
files: z55.array(OpenAiFileReference).max(10).optional().describe("Optional OpenAI-hosted file references provided by ChatGPT. They are only metadata for the UI bridge and are never forwarded to ProductMaker.")
|
|
@@ -14800,7 +14962,7 @@ __name(registerProductMakerPrompts, "registerProductMakerPrompts");
|
|
|
14800
14962
|
|
|
14801
14963
|
// src/server.ts
|
|
14802
14964
|
function createServer(opts) {
|
|
14803
|
-
const {
|
|
14965
|
+
const { apiBaseUrl, proxyBaseUrl = null, transport = "http", instructions = PRODUCTMAKER_SERVER_INSTRUCTIONS } = opts;
|
|
14804
14966
|
const server = new McpServer({
|
|
14805
14967
|
name: "productmaker",
|
|
14806
14968
|
version: MCP_VERSION
|
|
@@ -14811,9 +14973,11 @@ function createServer(opts) {
|
|
|
14811
14973
|
},
|
|
14812
14974
|
logging: {}
|
|
14813
14975
|
},
|
|
14814
|
-
instructions
|
|
14976
|
+
instructions
|
|
14977
|
+
});
|
|
14978
|
+
const api = new ApiClient(apiBaseUrl, opts.credential, {
|
|
14979
|
+
signal: opts.abortSignal
|
|
14815
14980
|
});
|
|
14816
|
-
const api = new ApiClient(apiBaseUrl, apiKey);
|
|
14817
14981
|
registerProductMakerPrompts(server);
|
|
14818
14982
|
registerMediaUploadAppResource(server, api.publicUrl("/v1/uploaded-assets/sessions/resolve"));
|
|
14819
14983
|
registerProductCostingTools(server);
|
|
@@ -14878,7 +15042,7 @@ async function main() {
|
|
|
14878
15042
|
process.exit(1);
|
|
14879
15043
|
}
|
|
14880
15044
|
const server = createServer({
|
|
14881
|
-
apiKey,
|
|
15045
|
+
credential: new StaticApiKeyCredential(apiKey),
|
|
14882
15046
|
apiBaseUrl,
|
|
14883
15047
|
transport: "stdio"
|
|
14884
15048
|
});
|