@diviops/mcp-server 1.5.17 → 1.5.19

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/README.md +2 -0
  2. package/dist/index.js +235 -4
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -84,6 +84,8 @@ Additional **conditionally-registered Pro tools** appear only on sites that have
84
84
  |----------|------------------|------------|
85
85
  | FluentCart reads (V1) | Pro plugin + FluentCart installed + module enabled | `diviops_fc_product_list`, `diviops_fc_product_get` |
86
86
  | FluentCart simple product writes (V2) | Pro plugin + FluentCart installed + module enabled | `diviops_fc_product_create`, `diviops_fc_product_update`, `diviops_fc_product_delete` |
87
+ | FluentCart variation read/write (V3) | Pro plugin + FluentCart installed + module enabled | `diviops_fc_variation_list`, `diviops_fc_variation_update` |
88
+ | FluentCart license-settings read/write (V3) | Pro plugin + FluentCart Pro installed + module enabled | `diviops_fc_license_settings_get`, `diviops_fc_license_settings_update` |
87
89
 
88
90
  When the gates are not satisfied, the tools simply don't appear on the MCP surface — no error envelope, no missing-capability hint. See the `diviops-fluentcart` skill bundle for the operator-side guide.
89
91
 
package/dist/index.js CHANGED
@@ -3156,7 +3156,7 @@ function registerProTools() {
3156
3156
  // surface can own multi-variant complexity cleanly.
3157
3157
  // diviops_fc_product_create — POST /diviops/v1/pro/fluentcart/products/create
3158
3158
  registerProTool("diviops_fc_product_create", {
3159
- description: "Create a simple FluentCart Pro product (Pro tier; requires FluentCart Pro installed + activated). V2 scope: simple onetime products only — one default variant, `detail.variation_type=\"simple\"`, `payment_type=\"onetime\"`, `fulfillment_type=\"digital\"|\"physical\"`. Multi-variation, subscriptions, downloadables, gallery, taxonomies, activation_limit, and license-flow fields ship in later verticals and are refused here. Required: `title` (1-200 chars). Optional: `status` (`draft`|`publish`|`pending`|`private`; default `draft`), `content`, `excerpt`, `fulfillment_type` (default `digital`), `price` (≥0; default 0), `compare_price` (≥0; must be ≥ `price` when provided), `sku` (unique across variations). Returns the standardized envelope { ok, data?, error: { code, message, hint? } }; apply-mode success payload is { product, detail, default_variation: { id, sku, item_price, compare_price } | null, variation_ids, variations_count, product_id, detail_id, default_variation_id } (HTTP 201). The default_variation block mirrors the diviops_fc_product_get read-back shape so callers don't need a follow-up read to confirm sku / compare_price / item_price after a write. Unit asymmetry: write inputs (price, compare_price) are in currency units (e.g. 29.99); the default_variation block returns item_price + compare_price in stored cents (e.g. 2999), matching detail.min_price / max_price. default_variation.compare_price is null when no compare price is stored (FCP persists \"no compare\" as 0, normalized to null on read); default_variation.sku is null when the SKU column is SQL NULL or an empty string (the create path stores omitted SKU as NULL). Error codes: invalid_input (400) when any input violates the constraints above; fluentcart.sku_conflict (409) when the provided SKU is already in use; fluentcart.module_inactive (412); fluentcart.command_failed (500) when wp_insert_post/ProductDetail/ProductVariation creation raises. Idempotency: NOT idempotent — repeat calls create distinct products." +
3159
+ description: "Create a simple FluentCart Pro product (Pro tier; requires FluentCart Pro installed + activated). V2 scope: simple onetime products only — one default variant, `detail.variation_type=\"simple\"`, `payment_type=\"onetime\"`, `fulfillment_type=\"digital\"|\"physical\"`. Multi-variation, subscriptions, downloadables, gallery, taxonomies, activation_limit, and license-flow fields ship in later verticals and are refused here. Required: `title` (1-200 chars). Optional: `status` (`draft`|`publish`|`pending`|`private`; default `draft`), `content`, `excerpt`, `fulfillment_type` (default `digital`), `price` (≥0; default 0), `compare_price` (≥0; must be ≥ `price` when provided), `sku` (optional; must be unique across FluentCart variations and at most 30 characters because FluentCart stores `fct_product_variations.sku` as `VARCHAR(30)` — overlong values are rejected before mutation rather than silently truncated). Returns the standardized envelope { ok, data?, error: { code, message, hint? } }; apply-mode success payload is { product, detail, default_variation: { id, sku, item_price, compare_price } | null, variation_ids, variations_count, product_id, detail_id, default_variation_id } (HTTP 201). The default_variation block mirrors the diviops_fc_product_get read-back shape so callers don't need a follow-up read to confirm sku / compare_price / item_price after a write. Unit asymmetry: write inputs (price, compare_price) are in currency units (e.g. 29.99); the default_variation block returns item_price + compare_price in stored cents (e.g. 2999), matching detail.min_price / max_price. default_variation.compare_price is null when no compare price is stored (FCP persists \"no compare\" as 0, normalized to null on read); default_variation.sku is null when the SKU column is SQL NULL or an empty string (the create path stores omitted SKU as NULL). Error codes: invalid_input (400) when any input violates the constraints above (including SKU > 30 chars — error.data carries { field: \"sku\", max_length: 30, actual_length }); fluentcart.sku_conflict (409) when the provided SKU is already in use; fluentcart.module_inactive (412); fluentcart.command_failed (500) when wp_insert_post/ProductDetail/ProductVariation creation raises. Idempotency: NOT idempotent — repeat calls create distinct products." +
3160
3160
  DRY_RUN_DESC_SUFFIX,
3161
3161
  inputSchema: {
3162
3162
  title: z
@@ -3193,7 +3193,7 @@ function registerProTools() {
3193
3193
  sku: z
3194
3194
  .string()
3195
3195
  .optional()
3196
- .describe("Default variation's SKU. Must be unique across all FluentCart variations. Omit to skip SKU assignment."),
3196
+ .describe("Default variation's SKU. Optional. Must be unique across all FluentCart variations and at most 30 characters (FluentCart stores fct_product_variations.sku as VARCHAR(30)). Omit to skip SKU assignment."),
3197
3197
  dry_run: DRY_RUN_FIELD,
3198
3198
  },
3199
3199
  annotations: { idempotentHint: false },
@@ -3228,7 +3228,7 @@ function registerProTools() {
3228
3228
  }, { target: "fluentcart", capabilityKey: "fluentcart_product_create" });
3229
3229
  // diviops_fc_product_update — POST /diviops/v1/pro/fluentcart/products/{id}/update
3230
3230
  registerProTool("diviops_fc_product_update", {
3231
- description: "Update a simple FluentCart Pro product (Pro tier; requires FluentCart Pro installed + activated). V2 scope: simple onetime products only — accepts partial updates on title, status, content, excerpt, fulfillment_type, price, compare_price, sku. Refuses non-simple products (variation_type other than 'simple', or default variant with payment_type other than 'onetime') with `fluentcart.unsupported_product_shape` (HTTP 422) — multi-variation + subscription writes ship in V3+. Required: `id` (positive integer; the post ID of the fluent_products CPT entry). All other fields optional; only changed fields are applied. When no field actually changes, returns `ok:true` with `data.noop: true` (apply mode) or an empty-plan dry-run summary. Returns the standardized envelope { ok, data?, error: { code, message, hint? } }; apply-mode success payload is { product, detail, default_variation: { id, sku, item_price, compare_price } | null, variation_ids, variations_count, changed_fields[] } (or { noop: true, product, detail, default_variation, ... } on a no-op). The default_variation block reflects the post-update state (or current state on noop) and mirrors the diviops_fc_product_get shape — sku, item_price, and compare_price round-trip without a follow-up read. Unit asymmetry: write inputs (price, compare_price) are in currency units (e.g. 29.99); default_variation returns item_price + compare_price in stored cents (e.g. 2999). default_variation.sku is null after `sku: \"\"` clears it (the empty string is stored as SQL NULL so the variations table's UNIQUE constraint allows multiple cleared SKUs); default_variation.compare_price is null when no compare price is stored (FCP persists \"no compare\" as 0, normalized to null on read). Error codes: invalid_input (400) when any field violates the constraints; not_found (404) when the product ID does not exist; fluentcart.unsupported_product_shape (422) when the product is not simple/onetime; fluentcart.sku_conflict (409) when a new SKU collides with another variation; fluentcart.module_inactive (412); fluentcart.command_failed (500). Idempotency: conditional — repeating an identical update is a no-op." +
3231
+ description: "Update a simple FluentCart Pro product (Pro tier; requires FluentCart Pro installed + activated). V2 scope: simple onetime products only — accepts partial updates on title, status, content, excerpt, fulfillment_type, price, compare_price, sku. SKU constraint: must be unique across FluentCart variations and at most 30 characters (FluentCart stores `fct_product_variations.sku` as `VARCHAR(30)`); `sku: \"\"` clears the SKU and reads back as `null`. Overlong SKUs are rejected before mutation rather than silently truncated. Refuses non-simple products (variation_type other than 'simple', or default variant with payment_type other than 'onetime') with `fluentcart.unsupported_product_shape` (HTTP 422) — multi-variation + subscription writes ship in V3+. Required: `id` (positive integer; the post ID of the fluent_products CPT entry). All other fields optional; only changed fields are applied. When no field actually changes, returns `ok:true` with `data.noop: true` (apply mode) or an empty-plan dry-run summary. Returns the standardized envelope { ok, data?, error: { code, message, hint? } }; apply-mode success payload is { product, detail, default_variation: { id, sku, item_price, compare_price } | null, variation_ids, variations_count, changed_fields[] } (or { noop: true, product, detail, default_variation, ... } on a no-op). The default_variation block reflects the post-update state (or current state on noop) and mirrors the diviops_fc_product_get shape — sku, item_price, and compare_price round-trip without a follow-up read. Unit asymmetry: write inputs (price, compare_price) are in currency units (e.g. 29.99); default_variation returns item_price + compare_price in stored cents (e.g. 2999). default_variation.sku is null after `sku: \"\"` clears it (the empty string is stored as SQL NULL so the variations table's UNIQUE constraint allows multiple cleared SKUs); default_variation.compare_price is null when no compare price is stored (FCP persists \"no compare\" as 0, normalized to null on read). Error codes: invalid_input (400) when any field violates the constraints (including SKU > 30 chars — error.data carries { field: \"sku\", max_length: 30, actual_length }); not_found (404) when the product ID does not exist; fluentcart.unsupported_product_shape (422) when the product is not simple/onetime; fluentcart.sku_conflict (409) when a new SKU collides with another variation; fluentcart.module_inactive (412); fluentcart.command_failed (500). Idempotency: conditional — repeating an identical update is a no-op." +
3232
3232
  DRY_RUN_DESC_SUFFIX,
3233
3233
  inputSchema: {
3234
3234
  id: z
@@ -3265,7 +3265,7 @@ function registerProTools() {
3265
3265
  sku: z
3266
3266
  .string()
3267
3267
  .optional()
3268
- .describe("New SKU for the default variation. Empty string clears the SKU."),
3268
+ .describe("New SKU for the default variation. Optional. Must be unique across all FluentCart variations and at most 30 characters (FluentCart stores fct_product_variations.sku as VARCHAR(30)). Empty string clears the SKU (reads back as null)."),
3269
3269
  dry_run: DRY_RUN_FIELD,
3270
3270
  },
3271
3271
  annotations: { idempotentHint: false },
@@ -3328,6 +3328,237 @@ function registerProTools() {
3328
3328
  ],
3329
3329
  };
3330
3330
  }, { target: "fluentcart", capabilityKey: "fluentcart_product_delete" });
3331
+ // ── V3 — variation read/write + license-settings read/write ────────
3332
+ //
3333
+ // V3 expands the FCP authoring surface so a draft simple-product catalog
3334
+ // can represent ADR-005 commercial shape: annual rows become subscription
3335
+ // products, lifetime rows stay onetime, and per-tier activation_limit
3336
+ // is writable. Activation limits live in `ProductMeta.license_settings`
3337
+ // per FluentCart Pro source (NOT variation `other_info`), so license
3338
+ // settings have their own dedicated read/write tools.
3339
+ //
3340
+ // V3 stays inside the simple-product default-variation contract:
3341
+ // no multi-variation create/delete, no signup-fee writes,
3342
+ // no license activation flow, no update-ZIP / readme / banner config.
3343
+ // diviops_fc_variation_list — POST /diviops/v1/pro/fluentcart/products/{id}/variations
3344
+ registerProTool("diviops_fc_variation_list", {
3345
+ description: "List FluentCart Pro variations for a product (Pro tier; V3; requires FluentCart Pro installed + activated). Read-only. Returns every variation row attached to the product with its subscription shape (payment_type, other_info.repeat_interval/times/trial_days/manage_setup_fee) and a license-settings projection when ProductMeta.license_settings is configured. Returns the standardized envelope { ok, data?, error: { code, message, hint? } }; success payload is { product_id, variation_type, default_variation_id, variations: VariationRow[], variations_count }. Each VariationRow carries { id, post_id, variation_title, sku, payment_type, item_price, compare_price, fulfillment_type, stock_status, manage_stock, available, other_info: { ...all stored keys... }, license: { activation_limit, validity: { unit, value } } | null }. Unit convention: item_price and compare_price are stored cents (e.g. 1900 = $19.00). compare_price is null when FCP stores the no-compare sentinel 0; sku is null when the column is SQL NULL OR an empty string. license is null when the product has no license_settings; otherwise activation_limit is null/'' (unset), 0 (unlimited per FluentCart Pro License::getActivationLimit), or a positive integer (max activations). validity.unit is one of: lifetime/day/week/month/year. Error codes: invalid_input (400) when id is not a positive integer; not_found (404) when the product does not exist; fluentcart.module_inactive (412); fluentcart.query_failed (500). Idempotency: read-only.",
3346
+ inputSchema: {
3347
+ product_id: z
3348
+ .number()
3349
+ .int()
3350
+ .positive()
3351
+ .describe("FluentCart product ID (the post ID of the fluent_products CPT entry)."),
3352
+ },
3353
+ annotations: { idempotentHint: true },
3354
+ _meta: { idempotent: "true" },
3355
+ }, async ({ product_id }) => {
3356
+ const result = await wp.requestEnveloped(`/pro/fluentcart/products/${product_id}/variations`, { method: "POST" });
3357
+ return {
3358
+ content: [
3359
+ {
3360
+ type: "text",
3361
+ text: serializeEnvelope(result, "diviops_fc_variation_list"),
3362
+ },
3363
+ ],
3364
+ };
3365
+ }, { target: "fluentcart", capabilityKey: "fluentcart_variation_list" });
3366
+ // diviops_fc_variation_update — POST /diviops/v1/pro/fluentcart/products/{product_id}/variations/{variation_id}/update
3367
+ registerProTool("diviops_fc_variation_update", {
3368
+ description: "Update the default variation of a simple FluentCart Pro product (Pro tier; V3; requires FluentCart Pro installed + activated). V3 scope: writes the product's default variation only; refuses non-simple products and non-default variations with `fluentcart.unsupported_product_shape` (HTTP 422). Multi-variation create/delete remains out of scope. Accepts partial updates on price, compare_price, sku, payment_type, and the subscription shape (repeat_interval, times, trial_days, manage_setup_fee). Switching `payment_type: \"subscription\"` requires `repeat_interval` (yearly/half_yearly/quarterly/monthly/weekly/daily) — either supplied in the same call or already stored. Switching to `onetime` strips the subscription-only keys from other_info, matching ProductVariationRequest::beforeValidation. `manage_setup_fee: \"yes\"` requires signup_fee + signup_fee_name which are out of scope for V3 (use FluentCart admin UI for setup fees); only `manage_setup_fee: \"no\"` is accepted. Returns the standardized envelope { ok, data?, error: { code, message, hint? } }; apply-mode success payload is { product_id, variation_id, changed_fields[], variation: VariationRow, product_price_range: { min_price, max_price } | null } (or { noop: true, product_id, variation_id, variation } when nothing changes). VariationRow mirrors the diviops_fc_variation_list shape — sku, item_price, compare_price, payment_type, other_info, license round-trip without a follow-up read. Unit asymmetry: write inputs (price, compare_price) are currency units (e.g. 19.00); VariationRow returns item_price + compare_price in stored cents. Error codes: invalid_input (400); not_found (404); fluentcart.unsupported_product_shape (422); fluentcart.sku_conflict (409); fluentcart.module_inactive (412); fluentcart.command_failed (500). Idempotency: conditional — identical repeat is a no-op." +
3369
+ DRY_RUN_DESC_SUFFIX,
3370
+ inputSchema: {
3371
+ product_id: z
3372
+ .number()
3373
+ .int()
3374
+ .positive()
3375
+ .describe("FluentCart product ID (the post ID of the fluent_products CPT entry)."),
3376
+ variation_id: z
3377
+ .number()
3378
+ .int()
3379
+ .positive()
3380
+ .describe("FluentCart variation ID. Must be the product's default variation (V3 constraint)."),
3381
+ price: z
3382
+ .number()
3383
+ .min(0)
3384
+ .optional()
3385
+ .describe("Variation item_price in currency units (e.g. 19.00). Non-negative."),
3386
+ compare_price: z
3387
+ .number()
3388
+ .min(0)
3389
+ .optional()
3390
+ .describe("Variation compare-at price in currency units. Must be ≥ price when both provided."),
3391
+ sku: z
3392
+ .string()
3393
+ .optional()
3394
+ .describe("Variation SKU. Must be unique across all FluentCart variations and at most 30 characters. Empty string clears the SKU (reads back as null)."),
3395
+ payment_type: z
3396
+ .enum(["onetime", "subscription"])
3397
+ .optional()
3398
+ .describe("Variation payment_type. Switching to 'subscription' requires repeat_interval. Switching to 'onetime' strips subscription-only fields from other_info."),
3399
+ repeat_interval: z
3400
+ .enum([
3401
+ "yearly",
3402
+ "half_yearly",
3403
+ "quarterly",
3404
+ "monthly",
3405
+ "weekly",
3406
+ "daily",
3407
+ ])
3408
+ .optional()
3409
+ .describe("Subscription billing interval. Required when switching to payment_type='subscription' unless already stored. For ADR-005 annual rows use 'yearly'."),
3410
+ times: z
3411
+ .number()
3412
+ .int()
3413
+ .min(0)
3414
+ .optional()
3415
+ .describe("Number of subscription billing cycles. 0 = indefinite (default for V3 subscription rows)."),
3416
+ trial_days: z
3417
+ .number()
3418
+ .int()
3419
+ .min(0)
3420
+ .max(365)
3421
+ .optional()
3422
+ .describe("Trial-period length in days (0-365)."),
3423
+ manage_setup_fee: z
3424
+ .enum(["no"])
3425
+ .optional()
3426
+ .describe("Setup-fee mode. V3 only accepts 'no' — manage_setup_fee='yes' requires signup_fee + signup_fee_name which are out of scope for V3."),
3427
+ dry_run: DRY_RUN_FIELD,
3428
+ },
3429
+ annotations: { idempotentHint: false },
3430
+ _meta: { idempotent: "conditional" },
3431
+ }, async ({ product_id, variation_id, price, compare_price, sku, payment_type, repeat_interval, times, trial_days, manage_setup_fee, dry_run, }) => {
3432
+ const body = {};
3433
+ if (price !== undefined)
3434
+ body.price = price;
3435
+ if (compare_price !== undefined)
3436
+ body.compare_price = compare_price;
3437
+ if (sku !== undefined)
3438
+ body.sku = sku;
3439
+ if (payment_type !== undefined)
3440
+ body.payment_type = payment_type;
3441
+ if (repeat_interval !== undefined)
3442
+ body.repeat_interval = repeat_interval;
3443
+ if (times !== undefined)
3444
+ body.times = times;
3445
+ if (trial_days !== undefined)
3446
+ body.trial_days = trial_days;
3447
+ if (manage_setup_fee !== undefined)
3448
+ body.manage_setup_fee = manage_setup_fee;
3449
+ if (dry_run !== undefined)
3450
+ body.dry_run = dry_run;
3451
+ const result = await wp.requestEnveloped(`/pro/fluentcart/products/${product_id}/variations/${variation_id}/update`, { method: "POST", body });
3452
+ return {
3453
+ content: [
3454
+ {
3455
+ type: "text",
3456
+ text: serializeEnvelope(result, "diviops_fc_variation_update"),
3457
+ },
3458
+ ],
3459
+ };
3460
+ }, { target: "fluentcart", capabilityKey: "fluentcart_variation_update" });
3461
+ // diviops_fc_license_settings_get — POST /diviops/v1/pro/fluentcart/products/{id}/license-settings
3462
+ registerProTool("diviops_fc_license_settings_get", {
3463
+ description: "Read the per-product FluentCart Pro license-settings projection (Pro tier; V3; requires FluentCart Pro installed + activated). FluentCart Pro stores license settings in `ProductMeta` under meta_key='license_settings'; this tool reads that meta row and joins it against the product's variations so each variation surfaces with its current activation_limit + validity. Read-only. Returns the standardized envelope { ok, data?, error: { code, message, hint? } }; success payload is { product_id, enabled: boolean, version, prefix, variations: [ { variation_id, title, activation_limit, validity: { unit, value } | null } ] }. Storage semantics: `enabled` is stored as 'yes'/'no' in FCP and projected to boolean here. `activation_limit` is null/'' (unconfigured), 0 (unlimited per FluentCart Pro License::getActivationLimit), or a positive integer. `validity.unit` is one of lifetime/day/week/month/year. Variations the product has but license_settings doesn't mention surface with `activation_limit: null` and `validity: null`. Variations license_settings mentions that no longer exist on the product are filtered out — only the live variation set is returned. Error codes: invalid_input (400) when id is not a positive integer; not_found (404) when the product does not exist; fluentcart.module_inactive (412); fluentcart.query_failed (500). Idempotency: read-only.",
3464
+ inputSchema: {
3465
+ product_id: z
3466
+ .number()
3467
+ .int()
3468
+ .positive()
3469
+ .describe("FluentCart product ID (the post ID of the fluent_products CPT entry)."),
3470
+ },
3471
+ annotations: { idempotentHint: true },
3472
+ _meta: { idempotent: "true" },
3473
+ }, async ({ product_id }) => {
3474
+ const result = await wp.requestEnveloped(`/pro/fluentcart/products/${product_id}/license-settings`, { method: "POST" });
3475
+ return {
3476
+ content: [
3477
+ {
3478
+ type: "text",
3479
+ text: serializeEnvelope(result, "diviops_fc_license_settings_get"),
3480
+ },
3481
+ ],
3482
+ };
3483
+ }, { target: "fluentcart", capabilityKey: "fluentcart_license_settings_get" });
3484
+ // diviops_fc_license_settings_update — POST /diviops/v1/pro/fluentcart/products/{id}/license-settings/update
3485
+ registerProTool("diviops_fc_license_settings_update", {
3486
+ description: "Write the per-product FluentCart Pro license-settings ProductMeta row (Pro tier; V3; requires FluentCart Pro installed + activated). Authors `enabled`, `version`, `prefix`, and per-variation `activation_limit` + `validity` — the storage shape FluentCart Pro reads via LicenseGenerationHandler when an order is placed. V3 explicitly skips configuring update-ZIP `global_update_file`, `wp` readme/banner/icon, downloadables, and the license-activation API; those fields are preserved when present but not authored. Refuses bundle products with `fluentcart.unsupported_product_shape` (HTTP 422). Inputs (all optional except product_id; partial updates supported): `enabled` (boolean — projected to FCP's 'yes'/'no' on write), `version` (required when enabling; max 50 chars), `prefix` (max 20 chars), `variations` (array of { variation_id (required), activation_limit (integer ≥ 0 or null; 0 = unlimited per FluentCart Pro License::getActivationLimit), validity (optional { unit: lifetime/day/week/month/year, value: positive integer } or null to auto-derive) }). When `validity` is omitted on a variation, the validity is derived from the variation's payment_type: subscription+yearly → { unit: 'year', value: 1 }; onetime → { unit: 'lifetime', value: 1 }. When `enabled: true` and the product carries variations, every variation must end up with a non-empty validity.unit. Returns the standardized envelope { ok, data?, error: { code, message, hint? } }; apply-mode success payload is { product_id, changed_fields[], license_settings: <same shape as diviops_fc_license_settings_get> } (or { noop: true, product_id, license_settings } on no-op). Error codes: invalid_input (400) when any field violates the constraints (unknown variation_id, negative activation_limit, missing version when enabling, missing validity.unit when enabling, bad enum value); not_found (404) when the product does not exist; fluentcart.unsupported_product_shape (422) on bundle products; fluentcart.module_inactive (412); fluentcart.command_failed (500). Idempotency: conditional — identical repeat is a no-op." +
3487
+ DRY_RUN_DESC_SUFFIX,
3488
+ inputSchema: {
3489
+ product_id: z
3490
+ .number()
3491
+ .int()
3492
+ .positive()
3493
+ .describe("FluentCart product ID (the post ID of the fluent_products CPT entry)."),
3494
+ enabled: z
3495
+ .boolean()
3496
+ .optional()
3497
+ .describe("Toggle FCP license-settings enablement. Stored as 'yes'/'no' in FCP. When true, version is required (use '1.0.0-beta' for the dogfood catalog)."),
3498
+ version: z
3499
+ .string()
3500
+ .max(50)
3501
+ .optional()
3502
+ .describe("License-settings version string (max 50 chars). Required when enabling. Recommended: '1.0.0-beta' for a beta-cohort catalog."),
3503
+ prefix: z
3504
+ .string()
3505
+ .max(20)
3506
+ .optional()
3507
+ .describe("License-key prefix (max 20 chars). Recommended: 'DOP' for DiviOps products."),
3508
+ variations: z
3509
+ .array(z.object({
3510
+ variation_id: z
3511
+ .number()
3512
+ .int()
3513
+ .positive()
3514
+ .describe("Target variation ID; must belong to this product."),
3515
+ activation_limit: z
3516
+ .number()
3517
+ .int()
3518
+ .min(0)
3519
+ .nullable()
3520
+ .optional()
3521
+ .describe("Activation limit. 0 = unlimited (per FluentCart Pro License::getActivationLimit). null clears the configured limit."),
3522
+ validity: z
3523
+ .object({
3524
+ unit: z.enum(["lifetime", "day", "week", "month", "year"]),
3525
+ value: z.number().int().positive(),
3526
+ })
3527
+ .nullable()
3528
+ .optional()
3529
+ .describe("Validity period. When omitted, derived from the variation's payment_type (subscription+yearly → year/1; onetime → lifetime/1). When null, force-rederived from current variation state."),
3530
+ }))
3531
+ .optional()
3532
+ .describe("Per-variation license configuration. Each entry's variation_id must belong to the product. Omitted variations preserve their existing license_settings."),
3533
+ dry_run: DRY_RUN_FIELD,
3534
+ },
3535
+ annotations: { idempotentHint: false },
3536
+ _meta: { idempotent: "conditional" },
3537
+ }, async ({ product_id, enabled, version, prefix, variations, dry_run, }) => {
3538
+ const body = {};
3539
+ if (enabled !== undefined)
3540
+ body.enabled = enabled;
3541
+ if (version !== undefined)
3542
+ body.version = version;
3543
+ if (prefix !== undefined)
3544
+ body.prefix = prefix;
3545
+ if (variations !== undefined)
3546
+ body.variations = variations;
3547
+ if (dry_run !== undefined)
3548
+ body.dry_run = dry_run;
3549
+ const result = await wp.requestEnveloped(`/pro/fluentcart/products/${product_id}/license-settings/update`, { method: "POST", body });
3550
+ return {
3551
+ content: [
3552
+ {
3553
+ type: "text",
3554
+ text: serializeEnvelope(result, "diviops_fc_license_settings_update"),
3555
+ },
3556
+ ],
3557
+ };
3558
+ }, {
3559
+ target: "fluentcart",
3560
+ capabilityKey: "fluentcart_license_settings_update",
3561
+ });
3331
3562
  }
3332
3563
  // ── Start ────────────────────────────────────────────────────────────
3333
3564
  async function main() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diviops/mcp-server",
3
- "version": "1.5.17",
3
+ "version": "1.5.19",
4
4
  "description": "MCP server exposing Divi 5 Visual Builder as tools for Claude",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",