@productmaker/mcp 11.7.0 → 11.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/dist/http.js +427 -43
  2. package/dist/stdio.js +427 -43
  3. package/package.json +1 -1
package/dist/http.js CHANGED
@@ -10012,20 +10012,287 @@ var TikTokAdvertiserSchema = z53.object({
10012
10012
  status: z53.string().optional(),
10013
10013
  timezone: z53.string().optional()
10014
10014
  });
10015
+ var TIKTOK_BUDGET_CURRENCY_POLICY = {
10016
+ USD: [
10017
+ 1,
10018
+ 0.01
10019
+ ],
10020
+ EUR: [
10021
+ 1,
10022
+ 0.01
10023
+ ],
10024
+ JPY: [
10025
+ 100,
10026
+ 1
10027
+ ],
10028
+ HKD: [
10029
+ 10,
10030
+ 0.01
10031
+ ],
10032
+ GBP: [
10033
+ 1,
10034
+ 0.01
10035
+ ],
10036
+ MYR: [
10037
+ 1,
10038
+ 0.01
10039
+ ],
10040
+ RUB: [
10041
+ 100,
10042
+ 0.01
10043
+ ],
10044
+ ZAR: [
10045
+ 10,
10046
+ 0.01
10047
+ ],
10048
+ KRW: [
10049
+ 1e3,
10050
+ 1
10051
+ ],
10052
+ AED: [
10053
+ 1,
10054
+ 0.01
10055
+ ],
10056
+ SAR: [
10057
+ 1,
10058
+ 0.01
10059
+ ],
10060
+ HUF: [
10061
+ 1,
10062
+ 1
10063
+ ],
10064
+ PLN: [
10065
+ 4,
10066
+ 0.01
10067
+ ],
10068
+ DKK: [
10069
+ 10,
10070
+ 0.01
10071
+ ],
10072
+ SEK: [
10073
+ 10,
10074
+ 0.01
10075
+ ],
10076
+ NOK: [
10077
+ 10,
10078
+ 0.01
10079
+ ],
10080
+ TRY: [
10081
+ 10,
10082
+ 0.01
10083
+ ],
10084
+ MXN: [
10085
+ 10,
10086
+ 0.01
10087
+ ],
10088
+ THB: [
10089
+ 10,
10090
+ 0.01
10091
+ ],
10092
+ AUD: [
10093
+ 1,
10094
+ 0.01
10095
+ ],
10096
+ CAD: [
10097
+ 1,
10098
+ 0.01
10099
+ ],
10100
+ NZD: [
10101
+ 1,
10102
+ 0.01
10103
+ ],
10104
+ SGD: [
10105
+ 1,
10106
+ 0.01
10107
+ ],
10108
+ CHF: [
10109
+ 1,
10110
+ 0.01
10111
+ ],
10112
+ CNY: [
10113
+ 10,
10114
+ 0.01
10115
+ ],
10116
+ DZD: [
10117
+ 100,
10118
+ 0.01
10119
+ ],
10120
+ ARS: [
10121
+ 1,
10122
+ 0.01
10123
+ ],
10124
+ BDT: [
10125
+ 100,
10126
+ 0.01
10127
+ ],
10128
+ BOB: [
10129
+ 10,
10130
+ 0.01
10131
+ ],
10132
+ BRL: [
10133
+ 1,
10134
+ 0.01
10135
+ ],
10136
+ CLP: [
10137
+ 1e3,
10138
+ 1
10139
+ ],
10140
+ COP: [
10141
+ 1e3,
10142
+ 0.01
10143
+ ],
10144
+ CRC: [
10145
+ 1e3,
10146
+ 0.01
10147
+ ],
10148
+ CZK: [
10149
+ 10,
10150
+ 0.01
10151
+ ],
10152
+ EGP: [
10153
+ 10,
10154
+ 0.01
10155
+ ],
10156
+ GTQ: [
10157
+ 10,
10158
+ 0.01
10159
+ ],
10160
+ HNL: [
10161
+ 10,
10162
+ 0.01
10163
+ ],
10164
+ ISK: [
10165
+ 100,
10166
+ 1
10167
+ ],
10168
+ INR: [
10169
+ 100,
10170
+ 0.01
10171
+ ],
10172
+ IDR: [
10173
+ 1e4,
10174
+ 1
10175
+ ],
10176
+ ILS: [
10177
+ 1,
10178
+ 0.01
10179
+ ],
10180
+ KES: [
10181
+ 100,
10182
+ 0.01
10183
+ ],
10184
+ MOP: [
10185
+ 10,
10186
+ 0.01
10187
+ ],
10188
+ TWD: [
10189
+ 10,
10190
+ 1
10191
+ ],
10192
+ NIO: [
10193
+ 10,
10194
+ 0.01
10195
+ ],
10196
+ NGN: [
10197
+ 100,
10198
+ 0.01
10199
+ ],
10200
+ PKR: [
10201
+ 100,
10202
+ 0.01
10203
+ ],
10204
+ PYG: [
10205
+ 1e4,
10206
+ 1
10207
+ ],
10208
+ PEN: [
10209
+ 1,
10210
+ 0.01
10211
+ ],
10212
+ PHP: [
10213
+ 50,
10214
+ 0.01
10215
+ ],
10216
+ QAR: [
10217
+ 1,
10218
+ 0.01
10219
+ ],
10220
+ RON: [
10221
+ 1,
10222
+ 0.01
10223
+ ],
10224
+ UAH: [
10225
+ 10,
10226
+ 0.01
10227
+ ],
10228
+ UYU: [
10229
+ 10,
10230
+ 0.01
10231
+ ],
10232
+ VEF: [
10233
+ 1e5,
10234
+ 0.01
10235
+ ],
10236
+ VND: [
10237
+ 1e4,
10238
+ 1
10239
+ ]
10240
+ };
10241
+ var TIKTOK_BUDGET_RULES_BY_CURRENCY = Object.freeze(Object.fromEntries(Object.entries(TIKTOK_BUDGET_CURRENCY_POLICY).map(([currency, policy]) => {
10242
+ if (!policy) throw new Error(`Missing TikTok budget policy for ${currency}`);
10243
+ const [ratio, precision] = policy;
10244
+ return [
10245
+ currency,
10246
+ Object.freeze({
10247
+ minimumDailyBudget: 20 * ratio,
10248
+ maximumDailyBudgetExclusive: 1e7 * ratio,
10249
+ precision
10250
+ })
10251
+ ];
10252
+ })));
10253
+ var TIKTOK_DAILY_BUDGET_BY_CURRENCY = Object.freeze(Object.fromEntries(Object.entries(TIKTOK_BUDGET_RULES_BY_CURRENCY).map(([currency, rule]) => [
10254
+ currency,
10255
+ rule?.minimumDailyBudget
10256
+ ])));
10257
+ var TikTokBudgetCurrencySchema = z53.string().trim().toUpperCase().regex(/^[A-Z]{3}$/);
10015
10258
  var TikTokObjectiveMode = z53.enum([
10016
10259
  "auto",
10017
10260
  "web_conversions",
10018
10261
  "traffic"
10019
10262
  ]);
10263
+ var TikTokCampaignDestinationSchema = z53.discriminatedUnion("type", [
10264
+ z53.object({
10265
+ type: z53.literal("landing")
10266
+ }),
10267
+ z53.object({
10268
+ type: z53.literal("whatsapp"),
10269
+ phoneNumber: z53.string().trim().regex(/^\+?[1-9][\d\s().-]{6,30}$/).refine((value) => /^\d{7,15}$/.test(value.replace(/\D/g, "")), "Use an international WhatsApp number"),
10270
+ /** Disambiguates shared calling codes such as +1. Independent of ad targeting. */
10271
+ phoneRegionCode: CountryIso2Schema.optional()
10272
+ })
10273
+ ]);
10020
10274
  var TikTokCampaignConfigSchema = z53.object({
10021
10275
  objectiveMode: TikTokObjectiveMode.default("auto"),
10022
10276
  /** Optional — service defaults to the currency-aware advertiser minimum
10023
- * when omitted (see TikTokService.createCampaignFromCreatives), so
10024
- * partial overrides ({targetCountry} only) don't 400 at validation. */
10025
- dailyBudget: z53.number().int().positive().optional(),
10277
+ * when omitted (see TikTokService.createCampaignFromCreatives), so
10278
+ * partial overrides ({targetCountry} only) don't 400 at validation. */
10279
+ dailyBudget: z53.number().finite().positive().optional(),
10280
+ /** Required with an explicit dailyBudget; validated against the advertiser. No FX conversion. */
10281
+ budgetCurrency: TikTokBudgetCurrencySchema.optional(),
10026
10282
  /** ISO-2 code or resolvable country name — normalized to alpha-2 at validation. */
10027
10283
  targetCountry: CountryIso2Schema.optional(),
10028
- campaignName: z53.string().trim().min(1).max(100).optional()
10284
+ campaignName: z53.string().trim().min(1).max(100).optional(),
10285
+ destination: TikTokCampaignDestinationSchema.optional()
10286
+ }).superRefine((value, ctx) => {
10287
+ if (value.dailyBudget !== void 0 && !value.budgetCurrency) {
10288
+ ctx.addIssue({
10289
+ code: z53.ZodIssueCode.custom,
10290
+ path: [
10291
+ "budgetCurrency"
10292
+ ],
10293
+ message: "TIKTOK_BUDGET_CURRENCY_REQUIRED"
10294
+ });
10295
+ }
10029
10296
  });
10030
10297
  var TikTokCreateCampaignBodySchema = z53.object({
10031
10298
  advertiserId: z53.string().min(1, "Debes seleccionar una cuenta publicitaria"),
@@ -10070,11 +10337,21 @@ var TikTokCreateCampaignFromCreativesBodySchema = z53.object({
10070
10337
  })).min(1).max(CAMPAIGN_LIMITS.MAX_CREATIVES),
10071
10338
  advertiserId: z53.string().min(1),
10072
10339
  pixelId: z53.string().optional(),
10073
- landingUrl: z53.string().url(),
10340
+ landingUrl: z53.string().url().optional(),
10074
10341
  /** ISO 3166-1 alpha-2 country code (country names are normalized to alpha-2) */
10075
10342
  targetCountry: CountryIso2Schema.default("US"),
10076
10343
  campaignName: z53.string().min(1).max(100).optional(),
10077
10344
  campaignConfig: TikTokCampaignConfigSchema.optional()
10345
+ }).superRefine((value, ctx) => {
10346
+ if (value.campaignConfig?.destination?.type !== "whatsapp" && !value.landingUrl) {
10347
+ ctx.addIssue({
10348
+ code: z53.ZodIssueCode.custom,
10349
+ path: [
10350
+ "landingUrl"
10351
+ ],
10352
+ message: "A landing URL is required for a website destination"
10353
+ });
10354
+ }
10078
10355
  });
10079
10356
  var TikTokCreateCampaignResponseSchema = z53.object({
10080
10357
  campaignId: z53.string(),
@@ -19931,37 +20208,47 @@ var PRODUCTMAKER_CAPABILITY_REGISTRY = deepFreeze([
19931
20208
  ], [
19932
20209
  "commerce_verification"
19933
20210
  ], MCP_ONLY_CHANNEL_EXPOSURE),
19934
- defineCapability("publish_to_tiktok", "89b0ffc96d195d8f9813b77a2d4b7724236a1524dfe8170347c6311547bd2e3e", "external_mutation", "publishing", [
20211
+ defineCapability(
20212
+ "publish_to_tiktok",
20213
+ // MCP 11.7.0: native WhatsApp destination and major-currency budget inputs.
20214
+ "50b932193b62929498ae342417427ef84b1982eb7620c6baffb62c8fb986e66e",
19935
20215
  "external_mutation",
19936
- "state_change"
19937
- ], [
19938
- slot("task", true, [
19939
- "taskId"
19940
- ]),
19941
- slot("advertiser", true, [
19942
- "advertiserId"
19943
- ]),
19944
- slot("tracking", false, [
19945
- "pixelId"
19946
- ]),
19947
- slot("landing", false, [
19948
- "landingUrl"
19949
- ]),
19950
- slot("creative_selection", false, [
19951
- "angleIndexes"
19952
- ]),
19953
- slot("campaign_configuration", false, [
19954
- "campaignConfig"
19955
- ]),
19956
- slot("ad_copy", false, [
19957
- "primaryText"
19958
- ])
19959
- ], [
19960
- "creative",
19961
- "tiktok_advertiser"
19962
- ], [
19963
- "tiktok_publication"
19964
- ]),
20216
+ "publishing",
20217
+ [
20218
+ "external_mutation",
20219
+ "state_change"
20220
+ ],
20221
+ [
20222
+ slot("task", true, [
20223
+ "taskId"
20224
+ ]),
20225
+ slot("advertiser", true, [
20226
+ "advertiserId"
20227
+ ]),
20228
+ slot("tracking", false, [
20229
+ "pixelId"
20230
+ ]),
20231
+ slot("landing", false, [
20232
+ "landingUrl"
20233
+ ]),
20234
+ slot("creative_selection", false, [
20235
+ "angleIndexes"
20236
+ ]),
20237
+ slot("campaign_configuration", false, [
20238
+ "campaignConfig"
20239
+ ]),
20240
+ slot("ad_copy", false, [
20241
+ "primaryText"
20242
+ ])
20243
+ ],
20244
+ [
20245
+ "creative",
20246
+ "tiktok_advertiser"
20247
+ ],
20248
+ [
20249
+ "tiktok_publication"
20250
+ ]
20251
+ ),
19965
20252
  defineCapability("prepare_winning_product_image", "6915790d22256a648374412ac4d11126fe3fabc9404f4552205057f9da3ce0db", "none", "business", [
19966
20253
  "state_change"
19967
20254
  ], [
@@ -22464,6 +22751,18 @@ var ServicePagePreviewResponseSchema = z104.object({
22464
22751
  artifact: ServiceLandingArtifactSchema,
22465
22752
  assetsById: z104.record(AssetIdSchema, ServicePagePublicAssetSchema)
22466
22753
  }).strict();
22754
+ var ServicePageAdminViewSchema = z104.object({
22755
+ id: z104.string().uuid(),
22756
+ businessName: z104.string().trim().min(1).max(120).nullable(),
22757
+ state: z104.enum([
22758
+ "created",
22759
+ "generating",
22760
+ "error"
22761
+ ]),
22762
+ publicUrl: z104.string().url().nullable(),
22763
+ artifact: ServiceLandingArtifactSchema.nullable(),
22764
+ assetsById: z104.record(AssetIdSchema, ServicePagePublicAssetSchema)
22765
+ }).strict();
22467
22766
  var ServicePagePreviewRequestSchema = z104.object({
22468
22767
  expectedRevision: z104.number().int().nonnegative(),
22469
22768
  candidateOperationId: z104.string().uuid().optional(),
@@ -22991,7 +23290,9 @@ var TaskAdDefaultsSchema = z113.object({
22991
23290
  ]).nullable(),
22992
23291
  headline: z113.string().min(1).max(AD_HEADLINE_MAX_CHARS).nullable(),
22993
23292
  /** ISO 3166-1 alpha-2 when known. */
22994
- country: z113.string().min(2).nullable()
23293
+ country: z113.string().min(2).nullable(),
23294
+ /** The task Brand's contact; optional for older API responses. */
23295
+ whatsappNumber: WhatsAppE164Schema.nullable().optional()
22995
23296
  });
22996
23297
 
22997
23298
  // ../../packages/types/src/service-website-capture.ts
@@ -25237,7 +25538,7 @@ var ApiClient = class {
25237
25538
  };
25238
25539
 
25239
25540
  // src/index.ts
25240
- var MCP_VERSION = "11.7.0";
25541
+ var MCP_VERSION = "11.8.0";
25241
25542
 
25242
25543
  // src/meta-upload-app.ts
25243
25544
  import { readFileSync } from "fs";
@@ -26332,6 +26633,13 @@ var McpErrorCode = {
26332
26633
  META_WHATSAPP_INVALID_NUMBER: "META_WHATSAPP_INVALID_NUMBER",
26333
26634
  META_INVALID_CTA: "META_INVALID_CTA",
26334
26635
  // TikTok Ads
26636
+ TIKTOK_SERVICE_VIDEO_REQUIRED: "TIKTOK_SERVICE_VIDEO_REQUIRED",
26637
+ TIKTOK_BUDGET_CURRENCY_REQUIRED: "TIKTOK_BUDGET_CURRENCY_REQUIRED",
26638
+ TIKTOK_BUDGET_CURRENCY_UNSUPPORTED: "TIKTOK_BUDGET_CURRENCY_UNSUPPORTED",
26639
+ TIKTOK_BUDGET_CURRENCY_MISMATCH: "TIKTOK_BUDGET_CURRENCY_MISMATCH",
26640
+ TIKTOK_BUDGET_TOO_LOW: "TIKTOK_BUDGET_TOO_LOW",
26641
+ TIKTOK_BUDGET_PRECISION_INVALID: "TIKTOK_BUDGET_PRECISION_INVALID",
26642
+ TIKTOK_BUDGET_TOO_HIGH: "TIKTOK_BUDGET_TOO_HIGH",
26335
26643
  TIKTOK_NOT_CONNECTED: "TIKTOK_NOT_CONNECTED",
26336
26644
  TIKTOK_TOKEN_EXPIRED: "TIKTOK_TOKEN_EXPIRED",
26337
26645
  TIKTOK_PERMISSION_DENIED: "TIKTOK_PERMISSION_DENIED",
@@ -26346,6 +26654,12 @@ var McpErrorCode = {
26346
26654
  TIKTOK_RATE_LIMITED: "TIKTOK_RATE_LIMITED",
26347
26655
  TIKTOK_SERVER_ERROR: "TIKTOK_SERVER_ERROR",
26348
26656
  TIKTOK_INVALID_DATA: "TIKTOK_INVALID_DATA",
26657
+ TIKTOK_WHATSAPP_INVALID_NUMBER: "TIKTOK_WHATSAPP_INVALID_NUMBER",
26658
+ TIKTOK_WHATSAPP_PHONE_REGION_REQUIRED: "TIKTOK_WHATSAPP_PHONE_REGION_REQUIRED",
26659
+ TIKTOK_WHATSAPP_INVALID_OBJECTIVE: "TIKTOK_WHATSAPP_INVALID_OBJECTIVE",
26660
+ TIKTOK_WHATSAPP_UNSUPPORTED_COUNTRY: "TIKTOK_WHATSAPP_UNSUPPORTED_COUNTRY",
26661
+ TIKTOK_WHATSAPP_VIDEO_REQUIRED: "TIKTOK_WHATSAPP_VIDEO_REQUIRED",
26662
+ TIKTOK_WHATSAPP_UNAVAILABLE: "TIKTOK_WHATSAPP_UNAVAILABLE",
26349
26663
  // Actor voice lifecycle (contextual voice cloning — the API throws these as
26350
26664
  // Nest ConflictException("VOICE_*"), so they arrive in body.message)
26351
26665
  VOICE_FEATURE_DISABLED: "VOICE_FEATURE_DISABLED",
@@ -26438,6 +26752,19 @@ var CODE_MESSAGES = {
26438
26752
  TIKTOK_RATE_LIMITED: "TikTok rate-limited the request. Wait a few minutes before retrying.",
26439
26753
  TIKTOK_SERVER_ERROR: "TikTok returned a server error. Retry in a few minutes.",
26440
26754
  TIKTOK_INVALID_DATA: "TikTok rejected the request due to invalid data. Verify `advertiserId`, `pixelId`, and `campaignConfig`.",
26755
+ TIKTOK_BUDGET_CURRENCY_REQUIRED: "Verify the selected advertiser currency and pass budgetCurrency with an explicit dailyBudget. If the account currency is unavailable, refresh advertiser access; never assume USD or reinterpret a cents amount.",
26756
+ TIKTOK_BUDGET_CURRENCY_UNSUPPORTED: "The selected advertiser currency has no verified TikTok budget policy. Do not guess a default or convert currencies.",
26757
+ TIKTOK_BUDGET_CURRENCY_MISMATCH: "The budget currency differs from the selected advertiser currency. dailyBudgetUsd supports USD accounts only; use dailyBudget plus the exact account budgetCurrency, without guessed FX.",
26758
+ TIKTOK_BUDGET_TOO_LOW: "The daily budget is below the provider minimum in account-currency major units. Ask for a valid explicit amount; never silently increase it.",
26759
+ TIKTOK_BUDGET_PRECISION_INVALID: "The daily budget exceeds the supported precision for the advertiser currency. Ask for an amount with the required precision.",
26760
+ TIKTOK_BUDGET_TOO_HIGH: "The daily budget meets or exceeds the exclusive provider maximum in account-currency major units. Ask for a lower explicit amount.",
26761
+ TIKTOK_SERVICE_VIDEO_REQUIRED: "Service image ads are supported on Meta. For TikTok, choose a service video; never silently omit an image from a mixed selected set.",
26762
+ TIKTOK_WHATSAPP_INVALID_NUMBER: "Use a complete international WhatsApp number including its calling code.",
26763
+ TIKTOK_WHATSAPP_PHONE_REGION_REQUIRED: "Ask the user for the country of this phone number and pass destination.phoneRegionCode. Do not assume it is the target country.",
26764
+ TIKTOK_WHATSAPP_INVALID_OBJECTIVE: "WhatsApp uses native click optimization. Choose a landing destination for web conversions.",
26765
+ TIKTOK_WHATSAPP_UNSUPPORTED_COUNTRY: "TikTok does not support native WhatsApp ad targeting in this country. Choose a supported country or a landing page.",
26766
+ TIKTOK_WHATSAPP_VIDEO_REQUIRED: "Native TikTok WhatsApp ads require video creatives. Choose a video or a landing destination for this image.",
26767
+ TIKTOK_WHATSAPP_UNAVAILABLE: "Verify the international WhatsApp number and ask TikTok to enable instant messaging ads for this advertiser. Do not retry as website traffic.",
26441
26768
  // Campaign (shared)
26442
26769
  CAMPAIGN_NOT_FOUND: "Campaign not found or does not belong to this user. Verify the `campaignId`.",
26443
26770
  CAMPAIGN_TASK_NOT_FOUND: "Campaign task not found. Verify the `taskId`.",
@@ -26562,6 +26889,19 @@ var INVALID_PARAM_CODES = /* @__PURE__ */ new Set([
26562
26889
  // Validation-shape backend errors (data-level rejection)
26563
26890
  McpErrorCode.META_INVALID_DATA,
26564
26891
  McpErrorCode.TIKTOK_INVALID_DATA,
26892
+ McpErrorCode.TIKTOK_SERVICE_VIDEO_REQUIRED,
26893
+ McpErrorCode.TIKTOK_BUDGET_CURRENCY_REQUIRED,
26894
+ McpErrorCode.TIKTOK_BUDGET_CURRENCY_UNSUPPORTED,
26895
+ McpErrorCode.TIKTOK_BUDGET_CURRENCY_MISMATCH,
26896
+ McpErrorCode.TIKTOK_BUDGET_TOO_LOW,
26897
+ McpErrorCode.TIKTOK_BUDGET_PRECISION_INVALID,
26898
+ McpErrorCode.TIKTOK_BUDGET_TOO_HIGH,
26899
+ McpErrorCode.TIKTOK_WHATSAPP_INVALID_NUMBER,
26900
+ McpErrorCode.TIKTOK_WHATSAPP_PHONE_REGION_REQUIRED,
26901
+ McpErrorCode.TIKTOK_WHATSAPP_INVALID_OBJECTIVE,
26902
+ McpErrorCode.TIKTOK_WHATSAPP_UNSUPPORTED_COUNTRY,
26903
+ McpErrorCode.TIKTOK_WHATSAPP_VIDEO_REQUIRED,
26904
+ McpErrorCode.TIKTOK_WHATSAPP_UNAVAILABLE,
26565
26905
  // Out-of-band user-resolvable auth/billing/connection — caller fixes by
26566
26906
  // going to Facebook/TikTok/Shopify and reconnecting, finishing
26567
26907
  // verification, or adding a payment method. None of these are transient,
@@ -26692,6 +27032,19 @@ var PASSTHROUGH_CODES = /* @__PURE__ */ new Set([
26692
27032
  McpErrorCode.TIKTOK_RATE_LIMITED,
26693
27033
  McpErrorCode.TIKTOK_SERVER_ERROR,
26694
27034
  McpErrorCode.TIKTOK_INVALID_DATA,
27035
+ McpErrorCode.TIKTOK_SERVICE_VIDEO_REQUIRED,
27036
+ McpErrorCode.TIKTOK_BUDGET_CURRENCY_REQUIRED,
27037
+ McpErrorCode.TIKTOK_BUDGET_CURRENCY_UNSUPPORTED,
27038
+ McpErrorCode.TIKTOK_BUDGET_CURRENCY_MISMATCH,
27039
+ McpErrorCode.TIKTOK_BUDGET_TOO_LOW,
27040
+ McpErrorCode.TIKTOK_BUDGET_PRECISION_INVALID,
27041
+ McpErrorCode.TIKTOK_BUDGET_TOO_HIGH,
27042
+ McpErrorCode.TIKTOK_WHATSAPP_INVALID_NUMBER,
27043
+ McpErrorCode.TIKTOK_WHATSAPP_PHONE_REGION_REQUIRED,
27044
+ McpErrorCode.TIKTOK_WHATSAPP_INVALID_OBJECTIVE,
27045
+ McpErrorCode.TIKTOK_WHATSAPP_UNSUPPORTED_COUNTRY,
27046
+ McpErrorCode.TIKTOK_WHATSAPP_VIDEO_REQUIRED,
27047
+ McpErrorCode.TIKTOK_WHATSAPP_UNAVAILABLE,
26695
27048
  // Campaign (shared)
26696
27049
  McpErrorCode.CAMPAIGN_NOT_FOUND,
26697
27050
  McpErrorCode.CAMPAIGN_TASK_NOT_FOUND,
@@ -31828,9 +32181,8 @@ function parseShopifyInput(rawInput) {
31828
32181
  }
31829
32182
  __name(parseShopifyInput, "parseShopifyInput");
31830
32183
  var McpCampaignConfig = z130.object({
31831
- /** ALWAYS in US dollars, whatever the ad account's currency is. ProductMaker
31832
- * converts to the account currency server-side. Do NOT pre-convert: on a COP
31833
- * account the user's "60.000 al día" is `dailyBudgetUsd: 15`, not 60000. */
32184
+ /** Legacy USD amount. Meta retains its existing conversion; the TikTok
32185
+ * schema below restricts this input to USD advertiser accounts. */
31834
32186
  dailyBudgetUsd: z130.number().positive().optional(),
31835
32187
  /** ISO-3166-1 alpha-2 country codes or resolvable names (CountryIso2Schema
31836
32188
  * normalizes at validation). Meta accepts max 1 (single-country ad set).
@@ -32057,13 +32409,38 @@ async function resolveAndUploadMedia(api, ref, expectedKind, assetRole, transpor
32057
32409
  return uploadResolvedMedia(api, media, assetRole);
32058
32410
  }
32059
32411
  __name(resolveAndUploadMedia, "resolveAndUploadMedia");
32412
+ var TikTokMcpCampaignConfig = McpCampaignConfig.extend({
32413
+ dailyBudgetUsd: z130.number().finite().positive().optional().describe("Legacy USD amount, supported only for USD advertiser accounts. No FX conversion."),
32414
+ dailyBudget: z130.number().finite().positive().optional().describe("Daily budget in the selected advertiser currency major units; pass budgetCurrency together."),
32415
+ budgetCurrency: TikTokBudgetCurrencySchema.optional(),
32416
+ destination: TikTokCampaignDestinationSchema.optional()
32417
+ }).superRefine((value, ctx) => {
32418
+ if (value.dailyBudgetUsd !== void 0 && (value.dailyBudget !== void 0 || value.budgetCurrency !== void 0)) {
32419
+ ctx.addIssue({
32420
+ code: z130.ZodIssueCode.custom,
32421
+ path: [
32422
+ "dailyBudgetUsd"
32423
+ ],
32424
+ message: "Choose either dailyBudgetUsd or dailyBudget with budgetCurrency, never both."
32425
+ });
32426
+ }
32427
+ if (value.dailyBudget !== void 0 !== (value.budgetCurrency !== void 0)) {
32428
+ ctx.addIssue({
32429
+ code: z130.ZodIssueCode.custom,
32430
+ path: [
32431
+ "budgetCurrency"
32432
+ ],
32433
+ message: "dailyBudget and budgetCurrency must be supplied together."
32434
+ });
32435
+ }
32436
+ });
32060
32437
  var TikTokInput = z130.object({
32061
32438
  taskId: z130.string().min(1),
32062
32439
  advertiserId: z130.string().min(1),
32063
32440
  pixelId: z130.string().min(1).optional(),
32064
32441
  landingUrl: z130.string().url().optional(),
32065
32442
  angleIndexes: z130.array(z130.number().int().nonnegative()).optional(),
32066
- campaignConfig: McpCampaignConfig.optional(),
32443
+ campaignConfig: TikTokMcpCampaignConfig.optional(),
32067
32444
  /** Override the ad's description (TikTok's body copy). If omitted,
32068
32445
  * ProductMaker uses the auto-generated copy attached to the task. */
32069
32446
  primaryText: z130.string().trim().min(1).max(500).optional()
@@ -32288,7 +32665,7 @@ function registerPublishTools(server, api, opts = {}) {
32288
32665
  }
32289
32666
  });
32290
32667
  server.registerTool("publish_to_tiktok", {
32291
- description: publishToTikTokGuidance.summary + " La campa\xF1a se crea SIEMPRE en estado PAUSED \u2014 el usuario la activa manualmente en TikTok Ads Manager. Use `list_tiktok_advertisers` antes para un advertiserId v\xE1lido. `pixelId` opcional pero recomendado para tracking. `angleIndexes` (opcional): selecciona qu\xE9 \xE1ngulos de la tarea original publicar \u2014 ej. [0,2]; omitir = publicar todos, incluidas las variantes creativas generadas despu\xE9s. `campaignConfig` (opcional): `dailyBudgetUsd` (presupuesto diario SIEMPRE en d\xF3lares, ej. 25 \u2014 nunca el n\xFAmero en moneda local), `targetCountries` (c\xF3digos ISO-2, primero se usa), `campaignName` (m\xE1x 100 chars). `primaryText` (opcional, m\xE1x 500 chars): override del copy del anuncio \u2014 solo \xFAsalo si el usuario te pas\xF3 un texto espec\xEDfico; si no, ProductMaker usa el copy auto-generado de la tarea. Despu\xE9s de publicar usa `get_tiktok_campaign_status` para chequear el estado.",
32668
+ description: publishToTikTokGuidance.summary + " Las tareas de servicios publican \xFAnicamente videos en TikTok; para im\xE1genes de servicios usa Meta Ads. No omitas im\xE1genes seleccionadas de una tarea mixta para forzar su publicaci\xF3n. La campa\xF1a se crea SIEMPRE en estado PAUSED \u2014 el usuario la activa manualmente en TikTok Ads Manager. Use `list_tiktok_advertisers` antes para un advertiserId v\xE1lido. `pixelId` opcional pero recomendado para tracking web. `campaignConfig.destination` acepta landing (default) o {type: 'whatsapp', phoneNumber: '+573001234567', phoneRegionCode?: 'CO'} para anuncios nativos de WhatsApp con videos. WhatsApp usa clics y no requiere landingUrl ni pixelId; phoneRegionCode identifica el pa\xEDs del tel\xE9fono y es obligatorio si el prefijo es compartido, como +1. No asumas que coincide con el pa\xEDs objetivo. TikTok exige habilitaci\xF3n de mensajer\xEDa para algunos advertisers; no sustituyas una denegaci\xF3n por tr\xE1fico web. `angleIndexes` (opcional): selecciona qu\xE9 \xE1ngulos de la tarea original publicar \u2014 ej. [0,2]; omitir = publicar todos, incluidas las variantes creativas generadas despu\xE9s. `campaignConfig` (opcional): `dailyBudget` + `budgetCurrency` (importe diario en unidades principales de la moneda de la cuenta, ej. 25.50 y USD, o 20000 y COP). `dailyBudgetUsd` es legado y solo sirve para cuentas USD; no se convierte FX. No env\xEDes ambos. Sin presupuesto expl\xEDcito se usa el m\xEDnimo documentado para la moneda real de la cuenta. `targetCountries` (c\xF3digos ISO-2, primero se usa), `campaignName` (m\xE1x 100 chars). `primaryText` (opcional, m\xE1x 500 chars): override del copy del anuncio \u2014 solo \xFAsalo si el usuario te pas\xF3 un texto espec\xEDfico; si no, ProductMaker usa el copy auto-generado de la tarea. Despu\xE9s de publicar usa `get_tiktok_campaign_status` para chequear el estado.",
32292
32669
  inputSchema: TikTokInput.shape,
32293
32670
  annotations: CREATES_EXTERNAL
32294
32671
  }, async ({ taskId, campaignConfig, primaryText, ...rest }) => {
@@ -32409,9 +32786,16 @@ function toMetaCampaignOverrides(cfg) {
32409
32786
  __name(toMetaCampaignOverrides, "toMetaCampaignOverrides");
32410
32787
  function toTikTokCampaignConfig(cfg) {
32411
32788
  const out = {};
32412
- if (cfg.dailyBudgetUsd !== void 0) out.dailyBudget = Math.round(cfg.dailyBudgetUsd * 100);
32789
+ if (cfg.dailyBudgetUsd !== void 0) {
32790
+ out.dailyBudget = cfg.dailyBudgetUsd;
32791
+ out.budgetCurrency = "USD";
32792
+ } else if (cfg.dailyBudget !== void 0) {
32793
+ out.dailyBudget = cfg.dailyBudget;
32794
+ out.budgetCurrency = cfg.budgetCurrency;
32795
+ }
32413
32796
  if (cfg.targetCountries && cfg.targetCountries.length > 0) out.targetCountry = cfg.targetCountries[0];
32414
32797
  if (cfg.campaignName !== void 0) out.campaignName = cfg.campaignName;
32798
+ if (cfg.destination !== void 0) out.destination = cfg.destination;
32415
32799
  return out;
32416
32800
  }
32417
32801
  __name(toTikTokCampaignConfig, "toTikTokCampaignConfig");
package/dist/stdio.js CHANGED
@@ -10034,20 +10034,287 @@ var TikTokAdvertiserSchema = z53.object({
10034
10034
  status: z53.string().optional(),
10035
10035
  timezone: z53.string().optional()
10036
10036
  });
10037
+ var TIKTOK_BUDGET_CURRENCY_POLICY = {
10038
+ USD: [
10039
+ 1,
10040
+ 0.01
10041
+ ],
10042
+ EUR: [
10043
+ 1,
10044
+ 0.01
10045
+ ],
10046
+ JPY: [
10047
+ 100,
10048
+ 1
10049
+ ],
10050
+ HKD: [
10051
+ 10,
10052
+ 0.01
10053
+ ],
10054
+ GBP: [
10055
+ 1,
10056
+ 0.01
10057
+ ],
10058
+ MYR: [
10059
+ 1,
10060
+ 0.01
10061
+ ],
10062
+ RUB: [
10063
+ 100,
10064
+ 0.01
10065
+ ],
10066
+ ZAR: [
10067
+ 10,
10068
+ 0.01
10069
+ ],
10070
+ KRW: [
10071
+ 1e3,
10072
+ 1
10073
+ ],
10074
+ AED: [
10075
+ 1,
10076
+ 0.01
10077
+ ],
10078
+ SAR: [
10079
+ 1,
10080
+ 0.01
10081
+ ],
10082
+ HUF: [
10083
+ 1,
10084
+ 1
10085
+ ],
10086
+ PLN: [
10087
+ 4,
10088
+ 0.01
10089
+ ],
10090
+ DKK: [
10091
+ 10,
10092
+ 0.01
10093
+ ],
10094
+ SEK: [
10095
+ 10,
10096
+ 0.01
10097
+ ],
10098
+ NOK: [
10099
+ 10,
10100
+ 0.01
10101
+ ],
10102
+ TRY: [
10103
+ 10,
10104
+ 0.01
10105
+ ],
10106
+ MXN: [
10107
+ 10,
10108
+ 0.01
10109
+ ],
10110
+ THB: [
10111
+ 10,
10112
+ 0.01
10113
+ ],
10114
+ AUD: [
10115
+ 1,
10116
+ 0.01
10117
+ ],
10118
+ CAD: [
10119
+ 1,
10120
+ 0.01
10121
+ ],
10122
+ NZD: [
10123
+ 1,
10124
+ 0.01
10125
+ ],
10126
+ SGD: [
10127
+ 1,
10128
+ 0.01
10129
+ ],
10130
+ CHF: [
10131
+ 1,
10132
+ 0.01
10133
+ ],
10134
+ CNY: [
10135
+ 10,
10136
+ 0.01
10137
+ ],
10138
+ DZD: [
10139
+ 100,
10140
+ 0.01
10141
+ ],
10142
+ ARS: [
10143
+ 1,
10144
+ 0.01
10145
+ ],
10146
+ BDT: [
10147
+ 100,
10148
+ 0.01
10149
+ ],
10150
+ BOB: [
10151
+ 10,
10152
+ 0.01
10153
+ ],
10154
+ BRL: [
10155
+ 1,
10156
+ 0.01
10157
+ ],
10158
+ CLP: [
10159
+ 1e3,
10160
+ 1
10161
+ ],
10162
+ COP: [
10163
+ 1e3,
10164
+ 0.01
10165
+ ],
10166
+ CRC: [
10167
+ 1e3,
10168
+ 0.01
10169
+ ],
10170
+ CZK: [
10171
+ 10,
10172
+ 0.01
10173
+ ],
10174
+ EGP: [
10175
+ 10,
10176
+ 0.01
10177
+ ],
10178
+ GTQ: [
10179
+ 10,
10180
+ 0.01
10181
+ ],
10182
+ HNL: [
10183
+ 10,
10184
+ 0.01
10185
+ ],
10186
+ ISK: [
10187
+ 100,
10188
+ 1
10189
+ ],
10190
+ INR: [
10191
+ 100,
10192
+ 0.01
10193
+ ],
10194
+ IDR: [
10195
+ 1e4,
10196
+ 1
10197
+ ],
10198
+ ILS: [
10199
+ 1,
10200
+ 0.01
10201
+ ],
10202
+ KES: [
10203
+ 100,
10204
+ 0.01
10205
+ ],
10206
+ MOP: [
10207
+ 10,
10208
+ 0.01
10209
+ ],
10210
+ TWD: [
10211
+ 10,
10212
+ 1
10213
+ ],
10214
+ NIO: [
10215
+ 10,
10216
+ 0.01
10217
+ ],
10218
+ NGN: [
10219
+ 100,
10220
+ 0.01
10221
+ ],
10222
+ PKR: [
10223
+ 100,
10224
+ 0.01
10225
+ ],
10226
+ PYG: [
10227
+ 1e4,
10228
+ 1
10229
+ ],
10230
+ PEN: [
10231
+ 1,
10232
+ 0.01
10233
+ ],
10234
+ PHP: [
10235
+ 50,
10236
+ 0.01
10237
+ ],
10238
+ QAR: [
10239
+ 1,
10240
+ 0.01
10241
+ ],
10242
+ RON: [
10243
+ 1,
10244
+ 0.01
10245
+ ],
10246
+ UAH: [
10247
+ 10,
10248
+ 0.01
10249
+ ],
10250
+ UYU: [
10251
+ 10,
10252
+ 0.01
10253
+ ],
10254
+ VEF: [
10255
+ 1e5,
10256
+ 0.01
10257
+ ],
10258
+ VND: [
10259
+ 1e4,
10260
+ 1
10261
+ ]
10262
+ };
10263
+ var TIKTOK_BUDGET_RULES_BY_CURRENCY = Object.freeze(Object.fromEntries(Object.entries(TIKTOK_BUDGET_CURRENCY_POLICY).map(([currency, policy]) => {
10264
+ if (!policy) throw new Error(`Missing TikTok budget policy for ${currency}`);
10265
+ const [ratio, precision] = policy;
10266
+ return [
10267
+ currency,
10268
+ Object.freeze({
10269
+ minimumDailyBudget: 20 * ratio,
10270
+ maximumDailyBudgetExclusive: 1e7 * ratio,
10271
+ precision
10272
+ })
10273
+ ];
10274
+ })));
10275
+ var TIKTOK_DAILY_BUDGET_BY_CURRENCY = Object.freeze(Object.fromEntries(Object.entries(TIKTOK_BUDGET_RULES_BY_CURRENCY).map(([currency, rule]) => [
10276
+ currency,
10277
+ rule?.minimumDailyBudget
10278
+ ])));
10279
+ var TikTokBudgetCurrencySchema = z53.string().trim().toUpperCase().regex(/^[A-Z]{3}$/);
10037
10280
  var TikTokObjectiveMode = z53.enum([
10038
10281
  "auto",
10039
10282
  "web_conversions",
10040
10283
  "traffic"
10041
10284
  ]);
10285
+ var TikTokCampaignDestinationSchema = z53.discriminatedUnion("type", [
10286
+ z53.object({
10287
+ type: z53.literal("landing")
10288
+ }),
10289
+ z53.object({
10290
+ type: z53.literal("whatsapp"),
10291
+ phoneNumber: z53.string().trim().regex(/^\+?[1-9][\d\s().-]{6,30}$/).refine((value) => /^\d{7,15}$/.test(value.replace(/\D/g, "")), "Use an international WhatsApp number"),
10292
+ /** Disambiguates shared calling codes such as +1. Independent of ad targeting. */
10293
+ phoneRegionCode: CountryIso2Schema.optional()
10294
+ })
10295
+ ]);
10042
10296
  var TikTokCampaignConfigSchema = z53.object({
10043
10297
  objectiveMode: TikTokObjectiveMode.default("auto"),
10044
10298
  /** Optional — service defaults to the currency-aware advertiser minimum
10045
- * when omitted (see TikTokService.createCampaignFromCreatives), so
10046
- * partial overrides ({targetCountry} only) don't 400 at validation. */
10047
- dailyBudget: z53.number().int().positive().optional(),
10299
+ * when omitted (see TikTokService.createCampaignFromCreatives), so
10300
+ * partial overrides ({targetCountry} only) don't 400 at validation. */
10301
+ dailyBudget: z53.number().finite().positive().optional(),
10302
+ /** Required with an explicit dailyBudget; validated against the advertiser. No FX conversion. */
10303
+ budgetCurrency: TikTokBudgetCurrencySchema.optional(),
10048
10304
  /** ISO-2 code or resolvable country name — normalized to alpha-2 at validation. */
10049
10305
  targetCountry: CountryIso2Schema.optional(),
10050
- campaignName: z53.string().trim().min(1).max(100).optional()
10306
+ campaignName: z53.string().trim().min(1).max(100).optional(),
10307
+ destination: TikTokCampaignDestinationSchema.optional()
10308
+ }).superRefine((value, ctx) => {
10309
+ if (value.dailyBudget !== void 0 && !value.budgetCurrency) {
10310
+ ctx.addIssue({
10311
+ code: z53.ZodIssueCode.custom,
10312
+ path: [
10313
+ "budgetCurrency"
10314
+ ],
10315
+ message: "TIKTOK_BUDGET_CURRENCY_REQUIRED"
10316
+ });
10317
+ }
10051
10318
  });
10052
10319
  var TikTokCreateCampaignBodySchema = z53.object({
10053
10320
  advertiserId: z53.string().min(1, "Debes seleccionar una cuenta publicitaria"),
@@ -10092,11 +10359,21 @@ var TikTokCreateCampaignFromCreativesBodySchema = z53.object({
10092
10359
  })).min(1).max(CAMPAIGN_LIMITS.MAX_CREATIVES),
10093
10360
  advertiserId: z53.string().min(1),
10094
10361
  pixelId: z53.string().optional(),
10095
- landingUrl: z53.string().url(),
10362
+ landingUrl: z53.string().url().optional(),
10096
10363
  /** ISO 3166-1 alpha-2 country code (country names are normalized to alpha-2) */
10097
10364
  targetCountry: CountryIso2Schema.default("US"),
10098
10365
  campaignName: z53.string().min(1).max(100).optional(),
10099
10366
  campaignConfig: TikTokCampaignConfigSchema.optional()
10367
+ }).superRefine((value, ctx) => {
10368
+ if (value.campaignConfig?.destination?.type !== "whatsapp" && !value.landingUrl) {
10369
+ ctx.addIssue({
10370
+ code: z53.ZodIssueCode.custom,
10371
+ path: [
10372
+ "landingUrl"
10373
+ ],
10374
+ message: "A landing URL is required for a website destination"
10375
+ });
10376
+ }
10100
10377
  });
10101
10378
  var TikTokCreateCampaignResponseSchema = z53.object({
10102
10379
  campaignId: z53.string(),
@@ -19706,37 +19983,47 @@ var PRODUCTMAKER_CAPABILITY_REGISTRY = deepFreeze([
19706
19983
  ], [
19707
19984
  "commerce_verification"
19708
19985
  ], MCP_ONLY_CHANNEL_EXPOSURE),
19709
- defineCapability("publish_to_tiktok", "89b0ffc96d195d8f9813b77a2d4b7724236a1524dfe8170347c6311547bd2e3e", "external_mutation", "publishing", [
19986
+ defineCapability(
19987
+ "publish_to_tiktok",
19988
+ // MCP 11.7.0: native WhatsApp destination and major-currency budget inputs.
19989
+ "50b932193b62929498ae342417427ef84b1982eb7620c6baffb62c8fb986e66e",
19710
19990
  "external_mutation",
19711
- "state_change"
19712
- ], [
19713
- slot("task", true, [
19714
- "taskId"
19715
- ]),
19716
- slot("advertiser", true, [
19717
- "advertiserId"
19718
- ]),
19719
- slot("tracking", false, [
19720
- "pixelId"
19721
- ]),
19722
- slot("landing", false, [
19723
- "landingUrl"
19724
- ]),
19725
- slot("creative_selection", false, [
19726
- "angleIndexes"
19727
- ]),
19728
- slot("campaign_configuration", false, [
19729
- "campaignConfig"
19730
- ]),
19731
- slot("ad_copy", false, [
19732
- "primaryText"
19733
- ])
19734
- ], [
19735
- "creative",
19736
- "tiktok_advertiser"
19737
- ], [
19738
- "tiktok_publication"
19739
- ]),
19991
+ "publishing",
19992
+ [
19993
+ "external_mutation",
19994
+ "state_change"
19995
+ ],
19996
+ [
19997
+ slot("task", true, [
19998
+ "taskId"
19999
+ ]),
20000
+ slot("advertiser", true, [
20001
+ "advertiserId"
20002
+ ]),
20003
+ slot("tracking", false, [
20004
+ "pixelId"
20005
+ ]),
20006
+ slot("landing", false, [
20007
+ "landingUrl"
20008
+ ]),
20009
+ slot("creative_selection", false, [
20010
+ "angleIndexes"
20011
+ ]),
20012
+ slot("campaign_configuration", false, [
20013
+ "campaignConfig"
20014
+ ]),
20015
+ slot("ad_copy", false, [
20016
+ "primaryText"
20017
+ ])
20018
+ ],
20019
+ [
20020
+ "creative",
20021
+ "tiktok_advertiser"
20022
+ ],
20023
+ [
20024
+ "tiktok_publication"
20025
+ ]
20026
+ ),
19740
20027
  defineCapability("prepare_winning_product_image", "6915790d22256a648374412ac4d11126fe3fabc9404f4552205057f9da3ce0db", "none", "business", [
19741
20028
  "state_change"
19742
20029
  ], [
@@ -22232,6 +22519,18 @@ var ServicePagePreviewResponseSchema = z104.object({
22232
22519
  artifact: ServiceLandingArtifactSchema,
22233
22520
  assetsById: z104.record(AssetIdSchema, ServicePagePublicAssetSchema)
22234
22521
  }).strict();
22522
+ var ServicePageAdminViewSchema = z104.object({
22523
+ id: z104.string().uuid(),
22524
+ businessName: z104.string().trim().min(1).max(120).nullable(),
22525
+ state: z104.enum([
22526
+ "created",
22527
+ "generating",
22528
+ "error"
22529
+ ]),
22530
+ publicUrl: z104.string().url().nullable(),
22531
+ artifact: ServiceLandingArtifactSchema.nullable(),
22532
+ assetsById: z104.record(AssetIdSchema, ServicePagePublicAssetSchema)
22533
+ }).strict();
22235
22534
  var ServicePagePreviewRequestSchema = z104.object({
22236
22535
  expectedRevision: z104.number().int().nonnegative(),
22237
22536
  candidateOperationId: z104.string().uuid().optional(),
@@ -22759,7 +23058,9 @@ var TaskAdDefaultsSchema = z113.object({
22759
23058
  ]).nullable(),
22760
23059
  headline: z113.string().min(1).max(AD_HEADLINE_MAX_CHARS).nullable(),
22761
23060
  /** ISO 3166-1 alpha-2 when known. */
22762
- country: z113.string().min(2).nullable()
23061
+ country: z113.string().min(2).nullable(),
23062
+ /** The task Brand's contact; optional for older API responses. */
23063
+ whatsappNumber: WhatsAppE164Schema.nullable().optional()
22763
23064
  });
22764
23065
 
22765
23066
  // ../../packages/types/src/service-website-capture.ts
@@ -23566,7 +23867,7 @@ var ApiClient = class {
23566
23867
  };
23567
23868
 
23568
23869
  // src/index.ts
23569
- var MCP_VERSION = "11.7.0";
23870
+ var MCP_VERSION = "11.8.0";
23570
23871
 
23571
23872
  // src/meta-upload-app.ts
23572
23873
  import { readFileSync } from "fs";
@@ -24661,6 +24962,13 @@ var McpErrorCode = {
24661
24962
  META_WHATSAPP_INVALID_NUMBER: "META_WHATSAPP_INVALID_NUMBER",
24662
24963
  META_INVALID_CTA: "META_INVALID_CTA",
24663
24964
  // TikTok Ads
24965
+ TIKTOK_SERVICE_VIDEO_REQUIRED: "TIKTOK_SERVICE_VIDEO_REQUIRED",
24966
+ TIKTOK_BUDGET_CURRENCY_REQUIRED: "TIKTOK_BUDGET_CURRENCY_REQUIRED",
24967
+ TIKTOK_BUDGET_CURRENCY_UNSUPPORTED: "TIKTOK_BUDGET_CURRENCY_UNSUPPORTED",
24968
+ TIKTOK_BUDGET_CURRENCY_MISMATCH: "TIKTOK_BUDGET_CURRENCY_MISMATCH",
24969
+ TIKTOK_BUDGET_TOO_LOW: "TIKTOK_BUDGET_TOO_LOW",
24970
+ TIKTOK_BUDGET_PRECISION_INVALID: "TIKTOK_BUDGET_PRECISION_INVALID",
24971
+ TIKTOK_BUDGET_TOO_HIGH: "TIKTOK_BUDGET_TOO_HIGH",
24664
24972
  TIKTOK_NOT_CONNECTED: "TIKTOK_NOT_CONNECTED",
24665
24973
  TIKTOK_TOKEN_EXPIRED: "TIKTOK_TOKEN_EXPIRED",
24666
24974
  TIKTOK_PERMISSION_DENIED: "TIKTOK_PERMISSION_DENIED",
@@ -24675,6 +24983,12 @@ var McpErrorCode = {
24675
24983
  TIKTOK_RATE_LIMITED: "TIKTOK_RATE_LIMITED",
24676
24984
  TIKTOK_SERVER_ERROR: "TIKTOK_SERVER_ERROR",
24677
24985
  TIKTOK_INVALID_DATA: "TIKTOK_INVALID_DATA",
24986
+ TIKTOK_WHATSAPP_INVALID_NUMBER: "TIKTOK_WHATSAPP_INVALID_NUMBER",
24987
+ TIKTOK_WHATSAPP_PHONE_REGION_REQUIRED: "TIKTOK_WHATSAPP_PHONE_REGION_REQUIRED",
24988
+ TIKTOK_WHATSAPP_INVALID_OBJECTIVE: "TIKTOK_WHATSAPP_INVALID_OBJECTIVE",
24989
+ TIKTOK_WHATSAPP_UNSUPPORTED_COUNTRY: "TIKTOK_WHATSAPP_UNSUPPORTED_COUNTRY",
24990
+ TIKTOK_WHATSAPP_VIDEO_REQUIRED: "TIKTOK_WHATSAPP_VIDEO_REQUIRED",
24991
+ TIKTOK_WHATSAPP_UNAVAILABLE: "TIKTOK_WHATSAPP_UNAVAILABLE",
24678
24992
  // Actor voice lifecycle (contextual voice cloning — the API throws these as
24679
24993
  // Nest ConflictException("VOICE_*"), so they arrive in body.message)
24680
24994
  VOICE_FEATURE_DISABLED: "VOICE_FEATURE_DISABLED",
@@ -24767,6 +25081,19 @@ var CODE_MESSAGES = {
24767
25081
  TIKTOK_RATE_LIMITED: "TikTok rate-limited the request. Wait a few minutes before retrying.",
24768
25082
  TIKTOK_SERVER_ERROR: "TikTok returned a server error. Retry in a few minutes.",
24769
25083
  TIKTOK_INVALID_DATA: "TikTok rejected the request due to invalid data. Verify `advertiserId`, `pixelId`, and `campaignConfig`.",
25084
+ TIKTOK_BUDGET_CURRENCY_REQUIRED: "Verify the selected advertiser currency and pass budgetCurrency with an explicit dailyBudget. If the account currency is unavailable, refresh advertiser access; never assume USD or reinterpret a cents amount.",
25085
+ TIKTOK_BUDGET_CURRENCY_UNSUPPORTED: "The selected advertiser currency has no verified TikTok budget policy. Do not guess a default or convert currencies.",
25086
+ TIKTOK_BUDGET_CURRENCY_MISMATCH: "The budget currency differs from the selected advertiser currency. dailyBudgetUsd supports USD accounts only; use dailyBudget plus the exact account budgetCurrency, without guessed FX.",
25087
+ TIKTOK_BUDGET_TOO_LOW: "The daily budget is below the provider minimum in account-currency major units. Ask for a valid explicit amount; never silently increase it.",
25088
+ TIKTOK_BUDGET_PRECISION_INVALID: "The daily budget exceeds the supported precision for the advertiser currency. Ask for an amount with the required precision.",
25089
+ TIKTOK_BUDGET_TOO_HIGH: "The daily budget meets or exceeds the exclusive provider maximum in account-currency major units. Ask for a lower explicit amount.",
25090
+ TIKTOK_SERVICE_VIDEO_REQUIRED: "Service image ads are supported on Meta. For TikTok, choose a service video; never silently omit an image from a mixed selected set.",
25091
+ TIKTOK_WHATSAPP_INVALID_NUMBER: "Use a complete international WhatsApp number including its calling code.",
25092
+ TIKTOK_WHATSAPP_PHONE_REGION_REQUIRED: "Ask the user for the country of this phone number and pass destination.phoneRegionCode. Do not assume it is the target country.",
25093
+ TIKTOK_WHATSAPP_INVALID_OBJECTIVE: "WhatsApp uses native click optimization. Choose a landing destination for web conversions.",
25094
+ TIKTOK_WHATSAPP_UNSUPPORTED_COUNTRY: "TikTok does not support native WhatsApp ad targeting in this country. Choose a supported country or a landing page.",
25095
+ TIKTOK_WHATSAPP_VIDEO_REQUIRED: "Native TikTok WhatsApp ads require video creatives. Choose a video or a landing destination for this image.",
25096
+ TIKTOK_WHATSAPP_UNAVAILABLE: "Verify the international WhatsApp number and ask TikTok to enable instant messaging ads for this advertiser. Do not retry as website traffic.",
24770
25097
  // Campaign (shared)
24771
25098
  CAMPAIGN_NOT_FOUND: "Campaign not found or does not belong to this user. Verify the `campaignId`.",
24772
25099
  CAMPAIGN_TASK_NOT_FOUND: "Campaign task not found. Verify the `taskId`.",
@@ -24891,6 +25218,19 @@ var INVALID_PARAM_CODES = /* @__PURE__ */ new Set([
24891
25218
  // Validation-shape backend errors (data-level rejection)
24892
25219
  McpErrorCode.META_INVALID_DATA,
24893
25220
  McpErrorCode.TIKTOK_INVALID_DATA,
25221
+ McpErrorCode.TIKTOK_SERVICE_VIDEO_REQUIRED,
25222
+ McpErrorCode.TIKTOK_BUDGET_CURRENCY_REQUIRED,
25223
+ McpErrorCode.TIKTOK_BUDGET_CURRENCY_UNSUPPORTED,
25224
+ McpErrorCode.TIKTOK_BUDGET_CURRENCY_MISMATCH,
25225
+ McpErrorCode.TIKTOK_BUDGET_TOO_LOW,
25226
+ McpErrorCode.TIKTOK_BUDGET_PRECISION_INVALID,
25227
+ McpErrorCode.TIKTOK_BUDGET_TOO_HIGH,
25228
+ McpErrorCode.TIKTOK_WHATSAPP_INVALID_NUMBER,
25229
+ McpErrorCode.TIKTOK_WHATSAPP_PHONE_REGION_REQUIRED,
25230
+ McpErrorCode.TIKTOK_WHATSAPP_INVALID_OBJECTIVE,
25231
+ McpErrorCode.TIKTOK_WHATSAPP_UNSUPPORTED_COUNTRY,
25232
+ McpErrorCode.TIKTOK_WHATSAPP_VIDEO_REQUIRED,
25233
+ McpErrorCode.TIKTOK_WHATSAPP_UNAVAILABLE,
24894
25234
  // Out-of-band user-resolvable auth/billing/connection — caller fixes by
24895
25235
  // going to Facebook/TikTok/Shopify and reconnecting, finishing
24896
25236
  // verification, or adding a payment method. None of these are transient,
@@ -25021,6 +25361,19 @@ var PASSTHROUGH_CODES = /* @__PURE__ */ new Set([
25021
25361
  McpErrorCode.TIKTOK_RATE_LIMITED,
25022
25362
  McpErrorCode.TIKTOK_SERVER_ERROR,
25023
25363
  McpErrorCode.TIKTOK_INVALID_DATA,
25364
+ McpErrorCode.TIKTOK_SERVICE_VIDEO_REQUIRED,
25365
+ McpErrorCode.TIKTOK_BUDGET_CURRENCY_REQUIRED,
25366
+ McpErrorCode.TIKTOK_BUDGET_CURRENCY_UNSUPPORTED,
25367
+ McpErrorCode.TIKTOK_BUDGET_CURRENCY_MISMATCH,
25368
+ McpErrorCode.TIKTOK_BUDGET_TOO_LOW,
25369
+ McpErrorCode.TIKTOK_BUDGET_PRECISION_INVALID,
25370
+ McpErrorCode.TIKTOK_BUDGET_TOO_HIGH,
25371
+ McpErrorCode.TIKTOK_WHATSAPP_INVALID_NUMBER,
25372
+ McpErrorCode.TIKTOK_WHATSAPP_PHONE_REGION_REQUIRED,
25373
+ McpErrorCode.TIKTOK_WHATSAPP_INVALID_OBJECTIVE,
25374
+ McpErrorCode.TIKTOK_WHATSAPP_UNSUPPORTED_COUNTRY,
25375
+ McpErrorCode.TIKTOK_WHATSAPP_VIDEO_REQUIRED,
25376
+ McpErrorCode.TIKTOK_WHATSAPP_UNAVAILABLE,
25024
25377
  // Campaign (shared)
25025
25378
  McpErrorCode.CAMPAIGN_NOT_FOUND,
25026
25379
  McpErrorCode.CAMPAIGN_TASK_NOT_FOUND,
@@ -30210,9 +30563,8 @@ function parseShopifyInput(rawInput) {
30210
30563
  }
30211
30564
  __name(parseShopifyInput, "parseShopifyInput");
30212
30565
  var McpCampaignConfig = z129.object({
30213
- /** ALWAYS in US dollars, whatever the ad account's currency is. ProductMaker
30214
- * converts to the account currency server-side. Do NOT pre-convert: on a COP
30215
- * account the user's "60.000 al día" is `dailyBudgetUsd: 15`, not 60000. */
30566
+ /** Legacy USD amount. Meta retains its existing conversion; the TikTok
30567
+ * schema below restricts this input to USD advertiser accounts. */
30216
30568
  dailyBudgetUsd: z129.number().positive().optional(),
30217
30569
  /** ISO-3166-1 alpha-2 country codes or resolvable names (CountryIso2Schema
30218
30570
  * normalizes at validation). Meta accepts max 1 (single-country ad set).
@@ -30439,13 +30791,38 @@ async function resolveAndUploadMedia(api, ref, expectedKind, assetRole, transpor
30439
30791
  return uploadResolvedMedia(api, media, assetRole);
30440
30792
  }
30441
30793
  __name(resolveAndUploadMedia, "resolveAndUploadMedia");
30794
+ var TikTokMcpCampaignConfig = McpCampaignConfig.extend({
30795
+ dailyBudgetUsd: z129.number().finite().positive().optional().describe("Legacy USD amount, supported only for USD advertiser accounts. No FX conversion."),
30796
+ dailyBudget: z129.number().finite().positive().optional().describe("Daily budget in the selected advertiser currency major units; pass budgetCurrency together."),
30797
+ budgetCurrency: TikTokBudgetCurrencySchema.optional(),
30798
+ destination: TikTokCampaignDestinationSchema.optional()
30799
+ }).superRefine((value, ctx) => {
30800
+ if (value.dailyBudgetUsd !== void 0 && (value.dailyBudget !== void 0 || value.budgetCurrency !== void 0)) {
30801
+ ctx.addIssue({
30802
+ code: z129.ZodIssueCode.custom,
30803
+ path: [
30804
+ "dailyBudgetUsd"
30805
+ ],
30806
+ message: "Choose either dailyBudgetUsd or dailyBudget with budgetCurrency, never both."
30807
+ });
30808
+ }
30809
+ if (value.dailyBudget !== void 0 !== (value.budgetCurrency !== void 0)) {
30810
+ ctx.addIssue({
30811
+ code: z129.ZodIssueCode.custom,
30812
+ path: [
30813
+ "budgetCurrency"
30814
+ ],
30815
+ message: "dailyBudget and budgetCurrency must be supplied together."
30816
+ });
30817
+ }
30818
+ });
30442
30819
  var TikTokInput = z129.object({
30443
30820
  taskId: z129.string().min(1),
30444
30821
  advertiserId: z129.string().min(1),
30445
30822
  pixelId: z129.string().min(1).optional(),
30446
30823
  landingUrl: z129.string().url().optional(),
30447
30824
  angleIndexes: z129.array(z129.number().int().nonnegative()).optional(),
30448
- campaignConfig: McpCampaignConfig.optional(),
30825
+ campaignConfig: TikTokMcpCampaignConfig.optional(),
30449
30826
  /** Override the ad's description (TikTok's body copy). If omitted,
30450
30827
  * ProductMaker uses the auto-generated copy attached to the task. */
30451
30828
  primaryText: z129.string().trim().min(1).max(500).optional()
@@ -30670,7 +31047,7 @@ function registerPublishTools(server, api, opts = {}) {
30670
31047
  }
30671
31048
  });
30672
31049
  server.registerTool("publish_to_tiktok", {
30673
- description: publishToTikTokGuidance.summary + " La campa\xF1a se crea SIEMPRE en estado PAUSED \u2014 el usuario la activa manualmente en TikTok Ads Manager. Use `list_tiktok_advertisers` antes para un advertiserId v\xE1lido. `pixelId` opcional pero recomendado para tracking. `angleIndexes` (opcional): selecciona qu\xE9 \xE1ngulos de la tarea original publicar \u2014 ej. [0,2]; omitir = publicar todos, incluidas las variantes creativas generadas despu\xE9s. `campaignConfig` (opcional): `dailyBudgetUsd` (presupuesto diario SIEMPRE en d\xF3lares, ej. 25 \u2014 nunca el n\xFAmero en moneda local), `targetCountries` (c\xF3digos ISO-2, primero se usa), `campaignName` (m\xE1x 100 chars). `primaryText` (opcional, m\xE1x 500 chars): override del copy del anuncio \u2014 solo \xFAsalo si el usuario te pas\xF3 un texto espec\xEDfico; si no, ProductMaker usa el copy auto-generado de la tarea. Despu\xE9s de publicar usa `get_tiktok_campaign_status` para chequear el estado.",
31050
+ description: publishToTikTokGuidance.summary + " Las tareas de servicios publican \xFAnicamente videos en TikTok; para im\xE1genes de servicios usa Meta Ads. No omitas im\xE1genes seleccionadas de una tarea mixta para forzar su publicaci\xF3n. La campa\xF1a se crea SIEMPRE en estado PAUSED \u2014 el usuario la activa manualmente en TikTok Ads Manager. Use `list_tiktok_advertisers` antes para un advertiserId v\xE1lido. `pixelId` opcional pero recomendado para tracking web. `campaignConfig.destination` acepta landing (default) o {type: 'whatsapp', phoneNumber: '+573001234567', phoneRegionCode?: 'CO'} para anuncios nativos de WhatsApp con videos. WhatsApp usa clics y no requiere landingUrl ni pixelId; phoneRegionCode identifica el pa\xEDs del tel\xE9fono y es obligatorio si el prefijo es compartido, como +1. No asumas que coincide con el pa\xEDs objetivo. TikTok exige habilitaci\xF3n de mensajer\xEDa para algunos advertisers; no sustituyas una denegaci\xF3n por tr\xE1fico web. `angleIndexes` (opcional): selecciona qu\xE9 \xE1ngulos de la tarea original publicar \u2014 ej. [0,2]; omitir = publicar todos, incluidas las variantes creativas generadas despu\xE9s. `campaignConfig` (opcional): `dailyBudget` + `budgetCurrency` (importe diario en unidades principales de la moneda de la cuenta, ej. 25.50 y USD, o 20000 y COP). `dailyBudgetUsd` es legado y solo sirve para cuentas USD; no se convierte FX. No env\xEDes ambos. Sin presupuesto expl\xEDcito se usa el m\xEDnimo documentado para la moneda real de la cuenta. `targetCountries` (c\xF3digos ISO-2, primero se usa), `campaignName` (m\xE1x 100 chars). `primaryText` (opcional, m\xE1x 500 chars): override del copy del anuncio \u2014 solo \xFAsalo si el usuario te pas\xF3 un texto espec\xEDfico; si no, ProductMaker usa el copy auto-generado de la tarea. Despu\xE9s de publicar usa `get_tiktok_campaign_status` para chequear el estado.",
30674
31051
  inputSchema: TikTokInput.shape,
30675
31052
  annotations: CREATES_EXTERNAL
30676
31053
  }, async ({ taskId, campaignConfig, primaryText, ...rest }) => {
@@ -30791,9 +31168,16 @@ function toMetaCampaignOverrides(cfg) {
30791
31168
  __name(toMetaCampaignOverrides, "toMetaCampaignOverrides");
30792
31169
  function toTikTokCampaignConfig(cfg) {
30793
31170
  const out = {};
30794
- if (cfg.dailyBudgetUsd !== void 0) out.dailyBudget = Math.round(cfg.dailyBudgetUsd * 100);
31171
+ if (cfg.dailyBudgetUsd !== void 0) {
31172
+ out.dailyBudget = cfg.dailyBudgetUsd;
31173
+ out.budgetCurrency = "USD";
31174
+ } else if (cfg.dailyBudget !== void 0) {
31175
+ out.dailyBudget = cfg.dailyBudget;
31176
+ out.budgetCurrency = cfg.budgetCurrency;
31177
+ }
30795
31178
  if (cfg.targetCountries && cfg.targetCountries.length > 0) out.targetCountry = cfg.targetCountries[0];
30796
31179
  if (cfg.campaignName !== void 0) out.campaignName = cfg.campaignName;
31180
+ if (cfg.destination !== void 0) out.destination = cfg.destination;
30797
31181
  return out;
30798
31182
  }
30799
31183
  __name(toTikTokCampaignConfig, "toTikTokCampaignConfig");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@productmaker/mcp",
3
- "version": "11.7.0",
3
+ "version": "11.8.0",
4
4
  "type": "module",
5
5
  "private": false,
6
6
  "description": "ProductMaker MCP server — cost products, generate landing pages, UGC videos, and ad creatives, and publish to Shopify, Meta Ads, and TikTok Ads.",