@productmaker/mcp 12.2.7 → 12.3.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.md CHANGED
@@ -207,7 +207,7 @@ its service-page editor, including its generation progress.
207
207
  | `import_orders_file` | Read the provider's spreadsheet from an ABSOLUTE path on the caller's machine and store its orders. **stdio only** — on the hosted server a path points at OUR disk, so it fails closed. Start with `dryRun: true` and tell the seller what it contains (how many orders, between which dates, which rows could not be read) before writing anything. No column with buyer data is ever read |
208
208
  | `create_operation` | Create the business of one country. The currency is derived from the country, never asked. Needed before anything else about the month: with no operation there is nothing for `get_finances` to assemble. Someone selling in a single country has one and never hears the word again |
209
209
  | `merge_products` | Merge two records of the same product into one. The two ids come from `get_product_control` — never invent them. The source DISAPPEARS and its orders move to the target, so confirm by NAME which one stays. It cannot be undone |
210
- | `get_finances` | Read-only monthly P&L: gross and delivered sales, freight, merchandise cost, gross profit, ad spend, fixed costs and NET profit **before taxes**, plus each rate against its target. Grouped on purpose: a model that needs six calls to answer "how am I doing?" assembles the picture itself between trips, and that is where it invents. `adSpendKnown: false` means ad spend could not be read and the profit is INCOMPLETE; `adSpendSource: "user"` means she corrected it by hand; `unconfirmedExpenses > 0` is inherited from last month and unreviewed. With one operation the server resolves it; with several it returns `operation_ambiguous` with the list |
210
+ | `get_finances` | Read-only monthly P&L before taxes: sales, costs, advertising, fixed costs and net profit. Unknown actual costs or advertising leave confirmed profit INCOMPLETE. Optional `costEstimate` uses current configured product costs: label it an ESTIMATE, never confirmed historical costs. Null/absent means no estimate. `adSpendSource: "user"` identifies manual correction; `unconfirmedExpenses > 0` flags inherited expenses. The server resolves a single operation or returns `operation_ambiguous`. |
211
211
  | `set_monthly_expense` | Save one FIXED cost of the month (rent, salaries, accountant). Writing the same label again REPLACES the amount — it does not add a second expense. Without fixed costs written, the month's profit always reads HIGHER than it is. Not for ad spend: that is `set_ad_spend` |
212
212
  | `set_ad_spend` | Correct by hand what was spent on ads that month. `amount: 0` ASSERTS "I did not advertise"; `null` WITHDRAWS the correction and gives the campaigns back the say. They are not the same |
213
213
  | `prepare_winning_product_image` | Prepare and show the exact photo of a user-selected winning-product candidate for free; securely stages it before the next photo choice. |
@@ -371,6 +371,20 @@ out on purpose: the integration key is a secret pasted in Integrations. All
371
371
  nine are MCP-only (`whatsapp: "unavailable"`) and behind the `financial-control`
372
372
  admin feature.
373
373
 
374
+ ### 12.2.8 — Result-viewer origin and correction delivery (unreleased)
375
+
376
+ - Declare the shared OpenAI component origin on listed and readable result-viewer
377
+ resources, matching the uploader. Keep the standard host-selected origin and
378
+ fail-closed media CSP unchanged for other MCP Apps hosts.
379
+ - SDK transport tests verify both metadata placements. Native acceptance and the
380
+ cause of a browser sandbox cancellation remain separate checks.
381
+ - Deliver published image corrections from `get_image_edit_status` through the
382
+ same read-only task viewer and protected asset proxy. Preserve operation status,
383
+ degradation and error; queued/processing stay pending and failed stays error.
384
+ Missing or invalid proxy/media configuration stays empty, with no raw storage
385
+ URL or storage key in model-facing content. The original task being done never
386
+ makes an unfinished correction ready. No generation or paid action is added.
387
+
374
388
  ### 12.2.7 — Upload reliability and media transport hardening (unreleased)
375
389
 
376
390
  - Correct public landing/Shopify publication and Meta creative URL/temporary-asset
package/dist/http.js CHANGED
@@ -3021,7 +3021,7 @@ var LlmPricingSnapshotSchema = z3.discriminatedUnion("source", [
3021
3021
  });
3022
3022
  function nativeOutputTokenCeiling(target) {
3023
3023
  if (target.nativeOwnedOutputPolicy) return target.nativeOwnedOutputPolicy.verifiedMaxOutputTokens;
3024
- return target.transport === "google" || target.id === "gemini-3.1-pro-openrouter" ? 65536 : 1e5;
3024
+ return target.transport === "google" || target.id === "gemini-flash-runware" || target.id === "gemini-3.1-pro-openrouter" ? 65536 : 1e5;
3025
3025
  }
3026
3026
  __name(nativeOutputTokenCeiling, "nativeOutputTokenCeiling");
3027
3027
  var LlmRouteTargetSchema = z3.object({
@@ -8288,6 +8288,12 @@ var CodeLandingAuthorEnvelopeSchema = z23.object({
8288
8288
  */
8289
8289
  variantFacts: z23.array(LandingVariantFactSchema).max(16).optional()
8290
8290
  });
8291
+ var CODE_LANDING_DEFERRED_AUTHOR_PROVIDER_RESULT_KIND = "code_landing_deferred_author_provider_v1";
8292
+ var CodeLandingDeferredAuthorProviderResultSchema = z23.object({
8293
+ kind: z23.literal(CODE_LANDING_DEFERRED_AUTHOR_PROVIDER_RESULT_KIND),
8294
+ stepKey: z23.string().min(1),
8295
+ providerResult: z23.record(z23.string(), z23.unknown())
8296
+ });
8291
8297
  var CodeLandingCommerceChangeSchema = z23.object({
8292
8298
  price: z23.string().trim().min(1).max(40).optional(),
8293
8299
  compareAtPrice: z23.string().trim().min(1).max(40).nullable().optional(),
@@ -10818,6 +10824,8 @@ var role = z38.object({
10818
10824
  provider: id2,
10819
10825
  model: id2,
10820
10826
  bindingId: id2,
10827
+ /** Immutable shared-router chain for fresh recipes; absent preserves legacy custody. */
10828
+ routeSnapshot: LlmRouteSnapshotSchema.optional(),
10821
10829
  /** null explicitly omits effort on OpenRouter; undefined is a historical recipe. */
10822
10830
  reasoning: CreativeEngineRoleReasoningSchema.nullable().optional(),
10823
10831
  /** Server-owned output ceiling; omitted historical recipes keep their old stage cap. */
@@ -10837,7 +10845,13 @@ var CreativeEngineRecipeSchema = z38.object({
10837
10845
  roles: z38.object({
10838
10846
  director: role,
10839
10847
  reviewer: role,
10840
- executor: role.nullable()
10848
+ executor: role.nullable(),
10849
+ /**
10850
+ * Independent reviewer of the assembled final video only (owner
10851
+ * decision 2026-10-26). Absent on historical recipes: their final
10852
+ * review keeps the frozen `reviewer`, unchanged hash and identity.
10853
+ */
10854
+ finalReviewer: role.optional()
10841
10855
  }).strict(),
10842
10856
  capabilities: z38.object({
10843
10857
  videoModels: z38.array(id2).min(1),
@@ -10847,6 +10861,8 @@ var CreativeEngineRecipeSchema = z38.object({
10847
10861
  "silent_sfx"
10848
10862
  ])).min(1),
10849
10863
  imageBackendAvailable: z38.boolean(),
10864
+ /** New journals may overlap independent accepted media under one coordinator. */
10865
+ mediaScheduling: z38.literal("bounded_parallel_v1").optional(),
10850
10866
  /** New server recipes require journaled speech QA; old receipts remain readable. */
10851
10867
  speechAssessment: z38.literal("whisper_v1").optional(),
10852
10868
  /** Frozen server review envelope; legacy readers retain their 48-frame limit. */
@@ -10856,9 +10872,14 @@ var CreativeEngineRecipeSchema = z38.object({
10856
10872
  /** New recipes reserve an explicit native failover; historical calls stay single-route. */
10857
10873
  modelFallbackPolicy: z38.literal("anthropic_conclusive_v1").optional(),
10858
10874
  /** New reviewer/executor calls use the shared same-model native provider router. */
10859
- centralModelRouting: z38.literal("native_owned_same_model_v1").optional(),
10875
+ centralModelRouting: z38.enum([
10876
+ "native_owned_same_model_v1",
10877
+ "shared_role_chain_v1"
10878
+ ]).optional(),
10860
10879
  /** Skip only a redundant global review of a deterministically frozen scene repair. */
10861
10880
  repairPlanReviewPolicy: z38.literal("frozen_contract_v1").optional(),
10881
+ /** Root-only visual patch wire contract; absent preserves historic full-plan recovery. */
10882
+ repairAuthoring: z38.literal("scene_patch_v1").optional(),
10862
10883
  /** New requests maximize output inside model and measured input headroom. */
10863
10884
  outputTokenPolicy: z38.literal("model_max_v1").optional(),
10864
10885
  /**
@@ -10878,6 +10899,8 @@ var CreativeEngineRecipeSchema = z38.object({
10878
10899
  explicitCastingPolicy: z38.literal("seller_explicit_v1").optional(),
10879
10900
  /** Frozen reviewer-only read model; absent keeps historical full request context. */
10880
10901
  reviewerContextPolicy: z38.literal("selected_commercial_v1").optional(),
10902
+ /** New final reviews explicitly account for selected critical technical findings. */
10903
+ finalReviewContextPolicy: z38.literal("selected_candidate_findings_v1").optional(),
10881
10904
  /** New voice requests after localized lengthening override stale timing only. */
10882
10905
  voiceLengthenDirectionPolicy: z38.literal("slot_duration_v1").optional(),
10883
10906
  /**
@@ -11010,6 +11033,8 @@ var CreativeEngineSceneSchema = z38.object({
11010
11033
  }).strict()
11011
11034
  ]),
11012
11035
  dependsOn: z38.array(id2),
11036
+ /** Subset of dependsOn consumed only as an approved human opening, never a final frame. */
11037
+ openingDependencies: z38.array(id2).optional(),
11013
11038
  visibleSpeaker: z38.enum([
11014
11039
  "human",
11015
11040
  "product",
@@ -11094,7 +11119,10 @@ var reviewUnavailabilityReason = z38.enum([
11094
11119
  "invalid_output",
11095
11120
  "reviewer_declined",
11096
11121
  /** #22: the gate's technical evidence could not be assembled after spend. */
11097
- "evidence_unavailable"
11122
+ "evidence_unavailable",
11123
+ /** No new purchase is allowed; an owned, verified final can still be delivered. */
11124
+ "cost_budget_exhausted",
11125
+ "post_budget_exhausted"
11098
11126
  ]);
11099
11127
  var CreativeEngineReviewSchema = z38.object({
11100
11128
  id: id2,
@@ -11319,6 +11347,7 @@ var CreativeEngineCandidateSchema = z38.object({
11319
11347
  qualityScore: z38.number().min(0).max(100)
11320
11348
  }).strict();
11321
11349
  var CreativeEngineCostBasisSchema = z38.enum([
11350
+ "reused",
11322
11351
  "provider_reported",
11323
11352
  "provider_credits_conversion",
11324
11353
  "catalogue_estimate"
@@ -11619,6 +11648,15 @@ var CreativeEngineEventSchema = z38.discriminatedUnion("type", [
11619
11648
  }).strict(),
11620
11649
  plan: CreativeEnginePlanSchema
11621
11650
  }).strict(),
11651
+ z38.object({
11652
+ type: z38.literal("accepted_author_output_revalidated"),
11653
+ operationId: id2,
11654
+ acceptedId: id2,
11655
+ context: CreativeEngineContextBindingSchema,
11656
+ outputSha256: hash,
11657
+ validationRevision: z38.string().regex(/^[a-z][a-z0-9_]{0,63}$/),
11658
+ plan: CreativeEnginePlanSchema
11659
+ }).strict(),
11622
11660
  z38.object({
11623
11661
  type: z38.literal("plan_recorded"),
11624
11662
  plan: CreativeEnginePlanSchema
@@ -13110,7 +13148,9 @@ var SERVICE_MODES = [
13110
13148
  ];
13111
13149
  var ServiceModeSchema = z46.enum(SERVICE_MODES);
13112
13150
  var AppFeaturesSchema = z46.object({
13113
- serviceMotionGraphics: z46.boolean()
13151
+ serviceMotionGraphics: z46.boolean(),
13152
+ /** GIF authoring controls; absent on older APIs means hidden. */
13153
+ landingGif: z46.boolean().default(false)
13114
13154
  });
13115
13155
  var MOTION_GRAPHICS_FORMATS = [
13116
13156
  "vertical",
@@ -22276,6 +22316,7 @@ var MonthlyExpensesQuerySchema = z97.object({
22276
22316
 
22277
22317
  // ../../packages/types/src/product-alias.ts
22278
22318
  import { z as z98 } from "zod";
22319
+ var PRODUCT_ALIAS_LABEL_MAX_LENGTH = 200;
22279
22320
  var PRODUCT_ALIAS_PROVIDERS = [
22280
22321
  "shopify",
22281
22322
  "dropi",
@@ -22292,12 +22333,12 @@ var ProductAliasSchema = z98.object({
22292
22333
  */
22293
22334
  externalId: z98.string().trim().min(1).max(64),
22294
22335
  /** El nombre con el que el archivo lo llamó, para que la persona lo reconozca. */
22295
- label: z98.string().trim().max(200).optional()
22336
+ label: z98.string().trim().max(PRODUCT_ALIAS_LABEL_MAX_LENGTH).optional()
22296
22337
  }).strict();
22297
22338
  var ResolveUnmatchedProductSchema = z98.object({
22298
22339
  provider: z98.enum(PRODUCT_ALIAS_PROVIDERS),
22299
22340
  externalId: z98.string().trim().min(1).max(64),
22300
- label: z98.string().trim().min(1).max(200),
22341
+ label: z98.string().trim().min(1).max(PRODUCT_ALIAS_LABEL_MAX_LENGTH),
22301
22342
  operationId: z98.string().uuid(),
22302
22343
  /** Presente ⇒ es el producto al que pertenece. Ausente ⇒ es uno nuevo. */
22303
22344
  productControlId: z98.string().uuid().optional()
@@ -22429,6 +22470,15 @@ var SyncShopifyOrdersSchema = z99.object({
22429
22470
 
22430
22471
  // ../../packages/types/src/product-control.ts
22431
22472
  import { z as z100 } from "zod";
22473
+ var MonthlyCostEstimateSchema = z100.object({
22474
+ source: z100.literal("product_costing"),
22475
+ linesEstimated: z100.number().int().positive(),
22476
+ merchandiseCost: z100.number().finite().nonnegative(),
22477
+ freightCost: z100.number().finite().nonnegative(),
22478
+ grossProfit: z100.number().finite(),
22479
+ netProfit: z100.number().finite(),
22480
+ netMarginOnDelivered: z100.number().finite().nullable()
22481
+ });
22432
22482
  var ProductControlDailyCountsSchema = z100.object({
22433
22483
  date: z100.string().regex(/^\d{4}-\d{2}-\d{2}$/),
22434
22484
  orders: z100.number().int().nullable(),
@@ -30873,11 +30923,12 @@ var ApiClient = class {
30873
30923
  };
30874
30924
 
30875
30925
  // src/index.ts
30876
- var MCP_VERSION = "12.2.7";
30926
+ var MCP_VERSION = "12.3.0";
30877
30927
 
30878
30928
  // src/meta-upload-app.ts
30879
30929
  import { readFileSync } from "fs";
30880
30930
  import { z as z132 } from "zod";
30931
+ import { registerAppResource, RESOURCE_MIME_TYPE } from "@modelcontextprotocol/ext-apps/server";
30881
30932
 
30882
30933
  // src/public-https.ts
30883
30934
  import { lookup } from "dns/promises";
@@ -31337,11 +31388,12 @@ function assertSafePublicBaseUrl(envName, url2) {
31337
31388
  }
31338
31389
  __name(assertSafePublicBaseUrl, "assertSafePublicBaseUrl");
31339
31390
 
31391
+ // src/app-origin.ts
31392
+ var PRODUCTMAKER_APP_ORIGIN = "https://productmaker.app";
31393
+
31340
31394
  // src/meta-upload-app.ts
31341
- import { registerAppResource, RESOURCE_MIME_TYPE } from "@modelcontextprotocol/ext-apps/server";
31342
31395
  var MEDIA_UPLOAD_APP_RESOURCE_URI = "ui://media-upload/v1.html";
31343
31396
  var MEDIA_UPLOAD_APP_NAME = "ProductMaker media upload";
31344
- var PRODUCTMAKER_APP_ORIGIN = "https://productmaker.app";
31345
31397
  var MEDIA_UPLOAD_APP_HTML_URL = new URL("./ui/meta-upload-app.html", import.meta.url);
31346
31398
  var PRODUCTMAKER_UPLOAD_STORAGE_ORIGIN = "https://825805a8113ddb98b68b9b0ddbb5d72a.r2.cloudflarestorage.com";
31347
31399
  var MEDIA_UPLOAD_CONNECT_DOMAINS = [
@@ -31541,6 +31593,7 @@ function registerTaskResultsAppResource(server, proxyBaseUrl) {
31541
31593
  connectDomains: []
31542
31594
  }
31543
31595
  },
31596
+ "openai/widgetDomain": PRODUCTMAKER_APP_ORIGIN,
31544
31597
  "openai/widgetCSP": {
31545
31598
  resource_domains: resourceDomains,
31546
31599
  connect_domains: []
@@ -37271,7 +37324,7 @@ function resolveLandingSetup(setup) {
37271
37324
  };
37272
37325
  }
37273
37326
  __name(resolveLandingSetup, "resolveLandingSetup");
37274
- function registerCreativeTools(server, api, { transport = "http" } = {}) {
37327
+ function registerCreativeTools(server, api, { transport = "http", proxyBaseUrl = null } = {}) {
37275
37328
  server.registerTool("generate_free_video", {
37276
37329
  title: "Custom-script video",
37277
37330
  description: generateFreeVideoGuidance.summary + ' Si el usuario adjunta o menciona una imagen local, llama primero request_media_upload con mediaKind=image y usa el assetId devuelto; no pidas path ni URL. Devuelve un taskId y procesa el job de forma as\xEDncrona.\n\nREQUERIDOS: rawPrompt, quality, durationSeconds y videoResolution.\n\nCALIDAD: "economy" = Econ\xF3mico, "medium" = Medio o "pro" = Pro (Wan). Economy admite 6/8/10/15s; Medium admite cualquier duraci\xF3n entera de 4 a 15s; Pro admite enteros de 2 a 30s y audio natural. `freeProModel` se elimin\xF3 \u2014 usa `modelId` (ver su descripci\xF3n); Seedance 2.5 est\xE1 inactivo y se rechaza con un error claro.\n\nSUBT\xCDTULOS: si addSubtitles=true, el rawPrompt completo se usa como texto exacto de subt\xEDtulos y debe tener m\xE1ximo 500 caracteres; no existe una pregunta adicional de transcripci\xF3n.\n\nPara videos normales con narrativa/\xE1ngulo usa generate_video_creative. Para tareas combinadas de producto usa create_product_task.',
@@ -37489,10 +37542,43 @@ function registerCreativeTools(server, api, { transport = "http" } = {}) {
37489
37542
  taskId: z142.string().uuid(),
37490
37543
  operationId: z142.string().uuid()
37491
37544
  },
37492
- annotations: READS_INTERNAL
37545
+ annotations: READS_INTERNAL,
37546
+ _meta: taskResultsToolMeta()
37493
37547
  }, async (input) => {
37494
37548
  try {
37495
- return asText(await api.getJson(`/v1/creatives/images/${encodeURIComponent(input.taskId)}/operations/${encodeURIComponent(input.operationId)}`));
37549
+ const { assetUrl, assetKey: _assetKey, ...operation } = ImageCreativeEditStatusResponse.parse(await api.getJson(`/v1/creatives/images/${encodeURIComponent(input.taskId)}/operations/${encodeURIComponent(input.operationId)}`));
37550
+ const imageExtension = assetUrl ? new URL(assetUrl).pathname.match(/\.(png|jpe?g|webp)$/i)?.[0]?.toLowerCase() : void 0;
37551
+ const proxyUrl = operation.status === "published" && assetUrl ? buildProxyUrl({
37552
+ r2Url: assetUrl,
37553
+ filename: `image-edit-${operation.variantIndex + 1}${imageExtension ?? ".bin"}`,
37554
+ proxyBaseUrl
37555
+ }) : null;
37556
+ const view = buildTaskResultsView({
37557
+ images: proxyUrl ? [
37558
+ {
37559
+ url: proxyUrl,
37560
+ alt: `Corrected image ${operation.variantIndex + 1}`
37561
+ }
37562
+ ] : [],
37563
+ videos: [],
37564
+ links: []
37565
+ }, proxyBaseUrl);
37566
+ if (operation.status === "failed") view.state = "error";
37567
+ else if (operation.status !== "published") view.state = "pending";
37568
+ const deliveredUrl = view.images[0]?.url;
37569
+ const response = {
37570
+ ...operation,
37571
+ ...deliveredUrl ? {
37572
+ assetUrl: deliveredUrl
37573
+ } : {}
37574
+ };
37575
+ return {
37576
+ ...asText(response),
37577
+ structuredContent: response,
37578
+ _meta: {
37579
+ taskResults: view
37580
+ }
37581
+ };
37496
37582
  } catch (error) {
37497
37583
  throw translateError(error);
37498
37584
  }
@@ -40932,7 +41018,7 @@ function registerProductControlTools(server, api, { transport = "http" } = {}) {
40932
41018
  }
40933
41019
  });
40934
41020
  server.registerTool("get_finances", {
40935
- description: getFinancesGuidance.summary + ' Contesta la pregunta que de verdad importa --- \xABeste negocio gana plata?\xBB --- con el mes armado: ventas totales y entregadas, fletes, costo de los productos, utilidad bruta, publicidad, gastos fijos y utilidad NETA. Esa utilidad es ANTES de impuestos y hay que decirlo asi: el contador es un gasto mas, no un calculo nuestro. Trae tambien los porcentajes contra sus metas. Si `deliveredSalesKnown` no es true, faltan importes de ventas entregadas: la utilidad y los m\xE1rgenes no est\xE1n completos. `grossSalesKnown` distinto de true indica ventas totales parciales. Si `costsKnown` viene en `false`, esos pedidos llegaron de una fuente que NO sabe cuanto costaron \u2014la tienda, o la API del proveedor\u2014 y la utilidad BRUTA esta incompleta: no es que el producto salga gratis. Si `adSpendKnown` viene en `false`, la publicidad NO se pudo leer y la utilidad esta INCOMPLETA: dilo antes de que alguien celebre un mes que perdio plata. `adSpendSource: "user"` significa que ella corrigio la cifra a mano, y eso se revisa distinto de una leida. Si `unconfirmedExpenses` es mayor que cero, ese monto viene heredado del mes pasado y ella no lo ha revisado. Es READ-ONLY: nunca pausa ni modifica una campana.',
41021
+ description: getFinancesGuidance.summary + ' Contesta la pregunta que de verdad importa --- \xABeste negocio gana plata?\xBB --- con el mes armado: ventas totales y entregadas, fletes, costo de los productos, utilidad bruta, publicidad, gastos fijos y utilidad NETA. Esa utilidad es ANTES de impuestos y hay que decirlo asi: el contador es un gasto mas, no un calculo nuestro. Trae tambien los porcentajes contra sus metas. Si `deliveredSalesKnown` no es true, faltan importes de ventas entregadas: la utilidad y los m\xE1rgenes no est\xE1n completos. `grossSalesKnown` distinto de true indica ventas totales parciales. Si `costsKnown` viene en `false`, esos pedidos llegaron de una fuente que NO sabe cuanto costaron \u2014la tienda, o la API del proveedor\u2014 y la utilidad BRUTA esta incompleta: no es que el producto salga gratis. Si `adSpendKnown` viene en `false`, la publicidad NO se pudo leer y la utilidad esta INCOMPLETA: dilo antes de que alguien celebre un mes que perdio plata. `adSpendSource: "user"` significa que ella corrigio la cifra a mano, y eso se revisa distinto de una leida. Si `unconfirmedExpenses` es mayor que cero, ese monto viene heredado del mes pasado y ella no lo ha revisado. Es READ-ONLY: nunca pausa ni modifica una campana. Si viene `costEstimate` con `source: "product_costing"`, presenta sus importes como ESTIMACI\xD3N antes de impuestos, usando los costos actuales configurados del producto: no son costos reales confirmados de esos pedidos. Conserva el aviso de `costsKnown: false` y distingue esta estimaci\xF3n de los importes reales del libro. `linesEstimated` indica cu\xE1ntas l\xEDneas usan costos configurados. La API s\xF3lo ofrece esta estimaci\xF3n cuando cubre todos los costos faltantes con perfiles asociados del mismo pa\xEDs y moneda, y conoce ventas entregadas, publicidad y gastos revisados. Si `costEstimate` es null o no existe, no inventes una estimaci\xF3n ni completes importes faltantes.',
40936
41022
  inputSchema: GetFinancesInput.shape,
40937
41023
  annotations: READS_INTERNAL
40938
41024
  }, async (input) => {
@@ -41413,6 +41499,7 @@ function createServer(opts) {
41413
41499
  registerServiceImageTools(server, api);
41414
41500
  registerAnimatedVideoTools(server, api);
41415
41501
  registerCreativeTools(server, api, {
41502
+ proxyBaseUrl,
41416
41503
  transport
41417
41504
  });
41418
41505
  registerCreativeVariantTools(server, api);
package/dist/stdio.js CHANGED
@@ -2796,7 +2796,7 @@ var LlmPricingSnapshotSchema = z3.discriminatedUnion("source", [
2796
2796
  });
2797
2797
  function nativeOutputTokenCeiling(target) {
2798
2798
  if (target.nativeOwnedOutputPolicy) return target.nativeOwnedOutputPolicy.verifiedMaxOutputTokens;
2799
- return target.transport === "google" || target.id === "gemini-3.1-pro-openrouter" ? 65536 : 1e5;
2799
+ return target.transport === "google" || target.id === "gemini-flash-runware" || target.id === "gemini-3.1-pro-openrouter" ? 65536 : 1e5;
2800
2800
  }
2801
2801
  __name(nativeOutputTokenCeiling, "nativeOutputTokenCeiling");
2802
2802
  var LlmRouteTargetSchema = z3.object({
@@ -8063,6 +8063,12 @@ var CodeLandingAuthorEnvelopeSchema = z23.object({
8063
8063
  */
8064
8064
  variantFacts: z23.array(LandingVariantFactSchema).max(16).optional()
8065
8065
  });
8066
+ var CODE_LANDING_DEFERRED_AUTHOR_PROVIDER_RESULT_KIND = "code_landing_deferred_author_provider_v1";
8067
+ var CodeLandingDeferredAuthorProviderResultSchema = z23.object({
8068
+ kind: z23.literal(CODE_LANDING_DEFERRED_AUTHOR_PROVIDER_RESULT_KIND),
8069
+ stepKey: z23.string().min(1),
8070
+ providerResult: z23.record(z23.string(), z23.unknown())
8071
+ });
8066
8072
  var CodeLandingCommerceChangeSchema = z23.object({
8067
8073
  price: z23.string().trim().min(1).max(40).optional(),
8068
8074
  compareAtPrice: z23.string().trim().min(1).max(40).nullable().optional(),
@@ -10593,6 +10599,8 @@ var role = z38.object({
10593
10599
  provider: id2,
10594
10600
  model: id2,
10595
10601
  bindingId: id2,
10602
+ /** Immutable shared-router chain for fresh recipes; absent preserves legacy custody. */
10603
+ routeSnapshot: LlmRouteSnapshotSchema.optional(),
10596
10604
  /** null explicitly omits effort on OpenRouter; undefined is a historical recipe. */
10597
10605
  reasoning: CreativeEngineRoleReasoningSchema.nullable().optional(),
10598
10606
  /** Server-owned output ceiling; omitted historical recipes keep their old stage cap. */
@@ -10612,7 +10620,13 @@ var CreativeEngineRecipeSchema = z38.object({
10612
10620
  roles: z38.object({
10613
10621
  director: role,
10614
10622
  reviewer: role,
10615
- executor: role.nullable()
10623
+ executor: role.nullable(),
10624
+ /**
10625
+ * Independent reviewer of the assembled final video only (owner
10626
+ * decision 2026-10-26). Absent on historical recipes: their final
10627
+ * review keeps the frozen `reviewer`, unchanged hash and identity.
10628
+ */
10629
+ finalReviewer: role.optional()
10616
10630
  }).strict(),
10617
10631
  capabilities: z38.object({
10618
10632
  videoModels: z38.array(id2).min(1),
@@ -10622,6 +10636,8 @@ var CreativeEngineRecipeSchema = z38.object({
10622
10636
  "silent_sfx"
10623
10637
  ])).min(1),
10624
10638
  imageBackendAvailable: z38.boolean(),
10639
+ /** New journals may overlap independent accepted media under one coordinator. */
10640
+ mediaScheduling: z38.literal("bounded_parallel_v1").optional(),
10625
10641
  /** New server recipes require journaled speech QA; old receipts remain readable. */
10626
10642
  speechAssessment: z38.literal("whisper_v1").optional(),
10627
10643
  /** Frozen server review envelope; legacy readers retain their 48-frame limit. */
@@ -10631,9 +10647,14 @@ var CreativeEngineRecipeSchema = z38.object({
10631
10647
  /** New recipes reserve an explicit native failover; historical calls stay single-route. */
10632
10648
  modelFallbackPolicy: z38.literal("anthropic_conclusive_v1").optional(),
10633
10649
  /** New reviewer/executor calls use the shared same-model native provider router. */
10634
- centralModelRouting: z38.literal("native_owned_same_model_v1").optional(),
10650
+ centralModelRouting: z38.enum([
10651
+ "native_owned_same_model_v1",
10652
+ "shared_role_chain_v1"
10653
+ ]).optional(),
10635
10654
  /** Skip only a redundant global review of a deterministically frozen scene repair. */
10636
10655
  repairPlanReviewPolicy: z38.literal("frozen_contract_v1").optional(),
10656
+ /** Root-only visual patch wire contract; absent preserves historic full-plan recovery. */
10657
+ repairAuthoring: z38.literal("scene_patch_v1").optional(),
10637
10658
  /** New requests maximize output inside model and measured input headroom. */
10638
10659
  outputTokenPolicy: z38.literal("model_max_v1").optional(),
10639
10660
  /**
@@ -10653,6 +10674,8 @@ var CreativeEngineRecipeSchema = z38.object({
10653
10674
  explicitCastingPolicy: z38.literal("seller_explicit_v1").optional(),
10654
10675
  /** Frozen reviewer-only read model; absent keeps historical full request context. */
10655
10676
  reviewerContextPolicy: z38.literal("selected_commercial_v1").optional(),
10677
+ /** New final reviews explicitly account for selected critical technical findings. */
10678
+ finalReviewContextPolicy: z38.literal("selected_candidate_findings_v1").optional(),
10656
10679
  /** New voice requests after localized lengthening override stale timing only. */
10657
10680
  voiceLengthenDirectionPolicy: z38.literal("slot_duration_v1").optional(),
10658
10681
  /**
@@ -10785,6 +10808,8 @@ var CreativeEngineSceneSchema = z38.object({
10785
10808
  }).strict()
10786
10809
  ]),
10787
10810
  dependsOn: z38.array(id2),
10811
+ /** Subset of dependsOn consumed only as an approved human opening, never a final frame. */
10812
+ openingDependencies: z38.array(id2).optional(),
10788
10813
  visibleSpeaker: z38.enum([
10789
10814
  "human",
10790
10815
  "product",
@@ -10869,7 +10894,10 @@ var reviewUnavailabilityReason = z38.enum([
10869
10894
  "invalid_output",
10870
10895
  "reviewer_declined",
10871
10896
  /** #22: the gate's technical evidence could not be assembled after spend. */
10872
- "evidence_unavailable"
10897
+ "evidence_unavailable",
10898
+ /** No new purchase is allowed; an owned, verified final can still be delivered. */
10899
+ "cost_budget_exhausted",
10900
+ "post_budget_exhausted"
10873
10901
  ]);
10874
10902
  var CreativeEngineReviewSchema = z38.object({
10875
10903
  id: id2,
@@ -11094,6 +11122,7 @@ var CreativeEngineCandidateSchema = z38.object({
11094
11122
  qualityScore: z38.number().min(0).max(100)
11095
11123
  }).strict();
11096
11124
  var CreativeEngineCostBasisSchema = z38.enum([
11125
+ "reused",
11097
11126
  "provider_reported",
11098
11127
  "provider_credits_conversion",
11099
11128
  "catalogue_estimate"
@@ -11394,6 +11423,15 @@ var CreativeEngineEventSchema = z38.discriminatedUnion("type", [
11394
11423
  }).strict(),
11395
11424
  plan: CreativeEnginePlanSchema
11396
11425
  }).strict(),
11426
+ z38.object({
11427
+ type: z38.literal("accepted_author_output_revalidated"),
11428
+ operationId: id2,
11429
+ acceptedId: id2,
11430
+ context: CreativeEngineContextBindingSchema,
11431
+ outputSha256: hash,
11432
+ validationRevision: z38.string().regex(/^[a-z][a-z0-9_]{0,63}$/),
11433
+ plan: CreativeEnginePlanSchema
11434
+ }).strict(),
11397
11435
  z38.object({
11398
11436
  type: z38.literal("plan_recorded"),
11399
11437
  plan: CreativeEnginePlanSchema
@@ -12885,7 +12923,9 @@ var SERVICE_MODES = [
12885
12923
  ];
12886
12924
  var ServiceModeSchema = z46.enum(SERVICE_MODES);
12887
12925
  var AppFeaturesSchema = z46.object({
12888
- serviceMotionGraphics: z46.boolean()
12926
+ serviceMotionGraphics: z46.boolean(),
12927
+ /** GIF authoring controls; absent on older APIs means hidden. */
12928
+ landingGif: z46.boolean().default(false)
12889
12929
  });
12890
12930
  var MOTION_GRAPHICS_FORMATS = [
12891
12931
  "vertical",
@@ -22051,6 +22091,7 @@ var MonthlyExpensesQuerySchema = z97.object({
22051
22091
 
22052
22092
  // ../../packages/types/src/product-alias.ts
22053
22093
  import { z as z98 } from "zod";
22094
+ var PRODUCT_ALIAS_LABEL_MAX_LENGTH = 200;
22054
22095
  var PRODUCT_ALIAS_PROVIDERS = [
22055
22096
  "shopify",
22056
22097
  "dropi",
@@ -22067,12 +22108,12 @@ var ProductAliasSchema = z98.object({
22067
22108
  */
22068
22109
  externalId: z98.string().trim().min(1).max(64),
22069
22110
  /** El nombre con el que el archivo lo llamó, para que la persona lo reconozca. */
22070
- label: z98.string().trim().max(200).optional()
22111
+ label: z98.string().trim().max(PRODUCT_ALIAS_LABEL_MAX_LENGTH).optional()
22071
22112
  }).strict();
22072
22113
  var ResolveUnmatchedProductSchema = z98.object({
22073
22114
  provider: z98.enum(PRODUCT_ALIAS_PROVIDERS),
22074
22115
  externalId: z98.string().trim().min(1).max(64),
22075
- label: z98.string().trim().min(1).max(200),
22116
+ label: z98.string().trim().min(1).max(PRODUCT_ALIAS_LABEL_MAX_LENGTH),
22076
22117
  operationId: z98.string().uuid(),
22077
22118
  /** Presente ⇒ es el producto al que pertenece. Ausente ⇒ es uno nuevo. */
22078
22119
  productControlId: z98.string().uuid().optional()
@@ -22204,6 +22245,15 @@ var SyncShopifyOrdersSchema = z99.object({
22204
22245
 
22205
22246
  // ../../packages/types/src/product-control.ts
22206
22247
  import { z as z100 } from "zod";
22248
+ var MonthlyCostEstimateSchema = z100.object({
22249
+ source: z100.literal("product_costing"),
22250
+ linesEstimated: z100.number().int().positive(),
22251
+ merchandiseCost: z100.number().finite().nonnegative(),
22252
+ freightCost: z100.number().finite().nonnegative(),
22253
+ grossProfit: z100.number().finite(),
22254
+ netProfit: z100.number().finite(),
22255
+ netMarginOnDelivered: z100.number().finite().nullable()
22256
+ });
22207
22257
  var ProductControlDailyCountsSchema = z100.object({
22208
22258
  date: z100.string().regex(/^\d{4}-\d{2}-\d{2}$/),
22209
22259
  orders: z100.number().int().nullable(),
@@ -29200,11 +29250,12 @@ var ApiClient = class {
29200
29250
  };
29201
29251
 
29202
29252
  // src/index.ts
29203
- var MCP_VERSION = "12.2.7";
29253
+ var MCP_VERSION = "12.3.0";
29204
29254
 
29205
29255
  // src/meta-upload-app.ts
29206
29256
  import { readFileSync } from "fs";
29207
29257
  import { z as z131 } from "zod";
29258
+ import { registerAppResource, RESOURCE_MIME_TYPE } from "@modelcontextprotocol/ext-apps/server";
29208
29259
 
29209
29260
  // src/public-https.ts
29210
29261
  import { lookup } from "dns/promises";
@@ -29668,11 +29719,12 @@ function assertSafeApiBaseUrl(url2) {
29668
29719
  }
29669
29720
  __name(assertSafeApiBaseUrl, "assertSafeApiBaseUrl");
29670
29721
 
29722
+ // src/app-origin.ts
29723
+ var PRODUCTMAKER_APP_ORIGIN = "https://productmaker.app";
29724
+
29671
29725
  // src/meta-upload-app.ts
29672
- import { registerAppResource, RESOURCE_MIME_TYPE } from "@modelcontextprotocol/ext-apps/server";
29673
29726
  var MEDIA_UPLOAD_APP_RESOURCE_URI = "ui://media-upload/v1.html";
29674
29727
  var MEDIA_UPLOAD_APP_NAME = "ProductMaker media upload";
29675
- var PRODUCTMAKER_APP_ORIGIN = "https://productmaker.app";
29676
29728
  var MEDIA_UPLOAD_APP_HTML_URL = new URL("./ui/meta-upload-app.html", import.meta.url);
29677
29729
  var PRODUCTMAKER_UPLOAD_STORAGE_ORIGIN = "https://825805a8113ddb98b68b9b0ddbb5d72a.r2.cloudflarestorage.com";
29678
29730
  var MEDIA_UPLOAD_CONNECT_DOMAINS = [
@@ -29872,6 +29924,7 @@ function registerTaskResultsAppResource(server, proxyBaseUrl) {
29872
29924
  connectDomains: []
29873
29925
  }
29874
29926
  },
29927
+ "openai/widgetDomain": PRODUCTMAKER_APP_ORIGIN,
29875
29928
  "openai/widgetCSP": {
29876
29929
  resource_domains: resourceDomains,
29877
29930
  connect_domains: []
@@ -35756,7 +35809,7 @@ function resolveLandingSetup(setup) {
35756
35809
  };
35757
35810
  }
35758
35811
  __name(resolveLandingSetup, "resolveLandingSetup");
35759
- function registerCreativeTools(server, api, { transport = "http" } = {}) {
35812
+ function registerCreativeTools(server, api, { transport = "http", proxyBaseUrl = null } = {}) {
35760
35813
  server.registerTool("generate_free_video", {
35761
35814
  title: "Custom-script video",
35762
35815
  description: generateFreeVideoGuidance.summary + ' Si el usuario adjunta o menciona una imagen local, llama primero request_media_upload con mediaKind=image y usa el assetId devuelto; no pidas path ni URL. Devuelve un taskId y procesa el job de forma as\xEDncrona.\n\nREQUERIDOS: rawPrompt, quality, durationSeconds y videoResolution.\n\nCALIDAD: "economy" = Econ\xF3mico, "medium" = Medio o "pro" = Pro (Wan). Economy admite 6/8/10/15s; Medium admite cualquier duraci\xF3n entera de 4 a 15s; Pro admite enteros de 2 a 30s y audio natural. `freeProModel` se elimin\xF3 \u2014 usa `modelId` (ver su descripci\xF3n); Seedance 2.5 est\xE1 inactivo y se rechaza con un error claro.\n\nSUBT\xCDTULOS: si addSubtitles=true, el rawPrompt completo se usa como texto exacto de subt\xEDtulos y debe tener m\xE1ximo 500 caracteres; no existe una pregunta adicional de transcripci\xF3n.\n\nPara videos normales con narrativa/\xE1ngulo usa generate_video_creative. Para tareas combinadas de producto usa create_product_task.',
@@ -35974,10 +36027,43 @@ function registerCreativeTools(server, api, { transport = "http" } = {}) {
35974
36027
  taskId: z141.string().uuid(),
35975
36028
  operationId: z141.string().uuid()
35976
36029
  },
35977
- annotations: READS_INTERNAL
36030
+ annotations: READS_INTERNAL,
36031
+ _meta: taskResultsToolMeta()
35978
36032
  }, async (input) => {
35979
36033
  try {
35980
- return asText(await api.getJson(`/v1/creatives/images/${encodeURIComponent(input.taskId)}/operations/${encodeURIComponent(input.operationId)}`));
36034
+ const { assetUrl, assetKey: _assetKey, ...operation } = ImageCreativeEditStatusResponse.parse(await api.getJson(`/v1/creatives/images/${encodeURIComponent(input.taskId)}/operations/${encodeURIComponent(input.operationId)}`));
36035
+ const imageExtension = assetUrl ? new URL(assetUrl).pathname.match(/\.(png|jpe?g|webp)$/i)?.[0]?.toLowerCase() : void 0;
36036
+ const proxyUrl = operation.status === "published" && assetUrl ? buildProxyUrl({
36037
+ r2Url: assetUrl,
36038
+ filename: `image-edit-${operation.variantIndex + 1}${imageExtension ?? ".bin"}`,
36039
+ proxyBaseUrl
36040
+ }) : null;
36041
+ const view = buildTaskResultsView({
36042
+ images: proxyUrl ? [
36043
+ {
36044
+ url: proxyUrl,
36045
+ alt: `Corrected image ${operation.variantIndex + 1}`
36046
+ }
36047
+ ] : [],
36048
+ videos: [],
36049
+ links: []
36050
+ }, proxyBaseUrl);
36051
+ if (operation.status === "failed") view.state = "error";
36052
+ else if (operation.status !== "published") view.state = "pending";
36053
+ const deliveredUrl = view.images[0]?.url;
36054
+ const response = {
36055
+ ...operation,
36056
+ ...deliveredUrl ? {
36057
+ assetUrl: deliveredUrl
36058
+ } : {}
36059
+ };
36060
+ return {
36061
+ ...asText(response),
36062
+ structuredContent: response,
36063
+ _meta: {
36064
+ taskResults: view
36065
+ }
36066
+ };
35981
36067
  } catch (error) {
35982
36068
  throw translateError(error);
35983
36069
  }
@@ -39417,7 +39503,7 @@ function registerProductControlTools(server, api, { transport = "http" } = {}) {
39417
39503
  }
39418
39504
  });
39419
39505
  server.registerTool("get_finances", {
39420
- description: getFinancesGuidance.summary + ' Contesta la pregunta que de verdad importa --- \xABeste negocio gana plata?\xBB --- con el mes armado: ventas totales y entregadas, fletes, costo de los productos, utilidad bruta, publicidad, gastos fijos y utilidad NETA. Esa utilidad es ANTES de impuestos y hay que decirlo asi: el contador es un gasto mas, no un calculo nuestro. Trae tambien los porcentajes contra sus metas. Si `deliveredSalesKnown` no es true, faltan importes de ventas entregadas: la utilidad y los m\xE1rgenes no est\xE1n completos. `grossSalesKnown` distinto de true indica ventas totales parciales. Si `costsKnown` viene en `false`, esos pedidos llegaron de una fuente que NO sabe cuanto costaron \u2014la tienda, o la API del proveedor\u2014 y la utilidad BRUTA esta incompleta: no es que el producto salga gratis. Si `adSpendKnown` viene en `false`, la publicidad NO se pudo leer y la utilidad esta INCOMPLETA: dilo antes de que alguien celebre un mes que perdio plata. `adSpendSource: "user"` significa que ella corrigio la cifra a mano, y eso se revisa distinto de una leida. Si `unconfirmedExpenses` es mayor que cero, ese monto viene heredado del mes pasado y ella no lo ha revisado. Es READ-ONLY: nunca pausa ni modifica una campana.',
39506
+ description: getFinancesGuidance.summary + ' Contesta la pregunta que de verdad importa --- \xABeste negocio gana plata?\xBB --- con el mes armado: ventas totales y entregadas, fletes, costo de los productos, utilidad bruta, publicidad, gastos fijos y utilidad NETA. Esa utilidad es ANTES de impuestos y hay que decirlo asi: el contador es un gasto mas, no un calculo nuestro. Trae tambien los porcentajes contra sus metas. Si `deliveredSalesKnown` no es true, faltan importes de ventas entregadas: la utilidad y los m\xE1rgenes no est\xE1n completos. `grossSalesKnown` distinto de true indica ventas totales parciales. Si `costsKnown` viene en `false`, esos pedidos llegaron de una fuente que NO sabe cuanto costaron \u2014la tienda, o la API del proveedor\u2014 y la utilidad BRUTA esta incompleta: no es que el producto salga gratis. Si `adSpendKnown` viene en `false`, la publicidad NO se pudo leer y la utilidad esta INCOMPLETA: dilo antes de que alguien celebre un mes que perdio plata. `adSpendSource: "user"` significa que ella corrigio la cifra a mano, y eso se revisa distinto de una leida. Si `unconfirmedExpenses` es mayor que cero, ese monto viene heredado del mes pasado y ella no lo ha revisado. Es READ-ONLY: nunca pausa ni modifica una campana. Si viene `costEstimate` con `source: "product_costing"`, presenta sus importes como ESTIMACI\xD3N antes de impuestos, usando los costos actuales configurados del producto: no son costos reales confirmados de esos pedidos. Conserva el aviso de `costsKnown: false` y distingue esta estimaci\xF3n de los importes reales del libro. `linesEstimated` indica cu\xE1ntas l\xEDneas usan costos configurados. La API s\xF3lo ofrece esta estimaci\xF3n cuando cubre todos los costos faltantes con perfiles asociados del mismo pa\xEDs y moneda, y conoce ventas entregadas, publicidad y gastos revisados. Si `costEstimate` es null o no existe, no inventes una estimaci\xF3n ni completes importes faltantes.',
39421
39507
  inputSchema: GetFinancesInput.shape,
39422
39508
  annotations: READS_INTERNAL
39423
39509
  }, async (input) => {
@@ -39958,6 +40044,7 @@ function createServer(opts) {
39958
40044
  registerServiceImageTools(server, api);
39959
40045
  registerAnimatedVideoTools(server, api);
39960
40046
  registerCreativeTools(server, api, {
40047
+ proxyBaseUrl,
39961
40048
  transport
39962
40049
  });
39963
40050
  registerCreativeVariantTools(server, api);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@productmaker/mcp",
3
- "version": "12.2.7",
3
+ "version": "12.3.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.",
@@ -67,8 +67,8 @@
67
67
  "typescript": "^7.0.2",
68
68
  "vitest": "5.0.3",
69
69
  "@packages/product-economics": "0.1.0",
70
- "@packages/types": "0.1.0",
71
- "@packages/utils": "0.1.0"
70
+ "@packages/utils": "0.1.0",
71
+ "@packages/types": "0.1.0"
72
72
  },
73
73
  "publishConfig": {
74
74
  "access": "public"