@diviops/mcp-server 1.5.19 → 1.5.21

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 +3 -0
  2. package/dist/index.js +405 -0
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -86,6 +86,9 @@ Additional **conditionally-registered Pro tools** appear only on sites that have
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
87
  | FluentCart variation read/write (V3) | Pro plugin + FluentCart installed + module enabled | `diviops_fc_variation_list`, `diviops_fc_variation_update` |
88
88
  | FluentCart license-settings read/write (V3) | Pro plugin + FluentCart Pro installed + module enabled | `diviops_fc_license_settings_get`, `diviops_fc_license_settings_update` |
89
+ | FluentCart order readback + guarded mark-paid (V3.1) | Pro plugin + FluentCart installed + module enabled | `diviops_fc_order_list`, `diviops_fc_order_get`, `diviops_fc_order_mark_paid` |
90
+ | FluentCart license readback (V3.1) | Pro plugin + FluentCart Pro installed + module enabled | `diviops_fc_license_list`, `diviops_fc_license_get`, `diviops_fc_license_activations_list` |
91
+ | FluentCart checkout readiness / gateway inspection (V3.2) | Pro plugin + FluentCart installed + module enabled | `diviops_fc_status`, `diviops_fc_gateway_list`, `diviops_fc_gateway_get` |
89
92
 
90
93
  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.
91
94
 
package/dist/index.js CHANGED
@@ -3559,6 +3559,411 @@ function registerProTools() {
3559
3559
  target: "fluentcart",
3560
3560
  capabilityKey: "fluentcart_license_settings_update",
3561
3561
  });
3562
+ // ── V3.1 — order/license/activation read + guarded mark-paid ───────
3563
+ //
3564
+ // FluentCart commerce-artifact readback surface plus a single
3565
+ // mutating tool: a guarded offline mark-paid that mirrors FCP's
3566
+ // OrderController::markAsPaid. Lifts the local checkout/license
3567
+ // smoke off of eval-file PHP probes.
3568
+ // diviops_fc_order_list — POST /diviops/v1/pro/fluentcart/orders
3569
+ registerProTool("diviops_fc_order_list", {
3570
+ description: "List FluentCart orders for commerce dogfooding / smoke baselines (Pro tier; V3.1; requires FluentCart installed + activated). Returns a paginated summary with order identity (id, status, payment_status), gateway info (payment_method + payment_method_title, mode), totals (currency, total_amount, total_paid), fulfillment_type, type (payment/subscription), customer (customer_id + customer_email), item_count, license_count, and timestamps (created_at, updated_at, completed_at). Filterable by status, payment_status, payment_method, product_id, customer_email, and mode (test/live). Read-only. Returns the standardized envelope { ok, data?, error: { code, message, hint? } }; the success payload is { orders: OrderSummary[], pagination: { page, per_page, total, total_pages }, filters: { ... } }. Error codes: invalid_input (HTTP 400) when status/payment_status/mode is out of range; fluentcart.module_inactive (HTTP 412); fluentcart.query_failed (HTTP 500). Use this alongside diviops_fc_order_get / diviops_fc_license_list to verify a smoke run without raw SQL probes.",
3571
+ inputSchema: {
3572
+ page: z
3573
+ .number()
3574
+ .int()
3575
+ .positive()
3576
+ .optional()
3577
+ .default(1)
3578
+ .describe("Page number, 1-indexed. Default 1."),
3579
+ per_page: z
3580
+ .number()
3581
+ .int()
3582
+ .positive()
3583
+ .optional()
3584
+ .default(20)
3585
+ .describe("Page size. Default 20, clamped to a max of 100."),
3586
+ status: z
3587
+ .enum([
3588
+ "on-hold",
3589
+ "pending",
3590
+ "processing",
3591
+ "completed",
3592
+ "canceled",
3593
+ "refunded",
3594
+ "failed",
3595
+ "draft",
3596
+ ])
3597
+ .optional()
3598
+ .describe("Order status filter (fct_orders.status). Match exact values such as 'completed' or 'on-hold'."),
3599
+ payment_status: z
3600
+ .enum([
3601
+ "pending",
3602
+ "paid",
3603
+ "partially_paid",
3604
+ "partially_refunded",
3605
+ "refunded",
3606
+ "failed",
3607
+ ])
3608
+ .optional()
3609
+ .describe("Payment status filter (fct_orders.payment_status)."),
3610
+ payment_method: z
3611
+ .string()
3612
+ .optional()
3613
+ .describe("Exact-match payment_method (e.g. 'offline_payment', 'stripe'). Case-sensitive — FluentCart stores gateway slugs verbatim."),
3614
+ product_id: z
3615
+ .number()
3616
+ .int()
3617
+ .positive()
3618
+ .optional()
3619
+ .describe("FluentCart product ID (post_id of the fluent_products CPT entry). Filters to orders with at least one matching order item."),
3620
+ customer_email: z
3621
+ .string()
3622
+ .optional()
3623
+ .describe("Filter to orders whose customer record has this email (exact match)."),
3624
+ mode: z
3625
+ .enum(["test", "live"])
3626
+ .optional()
3627
+ .describe("Filter to test-mode or live-mode orders. Useful for smoke runs to isolate the test gateway corpus."),
3628
+ },
3629
+ annotations: { idempotentHint: true },
3630
+ _meta: { idempotent: "true" },
3631
+ }, async ({ page, per_page, status, payment_status, payment_method, product_id, customer_email, mode, }) => {
3632
+ const body = {};
3633
+ if (page !== undefined)
3634
+ body.page = page;
3635
+ if (per_page !== undefined)
3636
+ body.per_page = per_page;
3637
+ if (status !== undefined)
3638
+ body.status = status;
3639
+ if (payment_status !== undefined)
3640
+ body.payment_status = payment_status;
3641
+ if (payment_method !== undefined)
3642
+ body.payment_method = payment_method;
3643
+ if (product_id !== undefined)
3644
+ body.product_id = product_id;
3645
+ if (customer_email !== undefined)
3646
+ body.customer_email = customer_email;
3647
+ if (mode !== undefined)
3648
+ body.mode = mode;
3649
+ const result = await wp.requestEnveloped("/pro/fluentcart/orders", {
3650
+ method: "POST",
3651
+ body,
3652
+ });
3653
+ return {
3654
+ content: [
3655
+ {
3656
+ type: "text",
3657
+ text: serializeEnvelope(result, "diviops_fc_order_list"),
3658
+ },
3659
+ ],
3660
+ };
3661
+ }, { target: "fluentcart", capabilityKey: "fluentcart_order_list" });
3662
+ // diviops_fc_order_get — POST /diviops/v1/pro/fluentcart/orders/{id}
3663
+ registerProTool("diviops_fc_order_get", {
3664
+ description: "Fetch a single FluentCart order with line items, transactions, and related license IDs (Pro tier; V3.1; requires FluentCart installed + activated). Read-only. Returns the standardized envelope { ok, data?, error: { code, message, hint? } }; the success payload is { order: OrderSummary, items: OrderItem[], transactions: Transaction[], license_ids: number[] }. OrderItem includes id, post_id (product CPT post_id), object_id (variation_id), title, quantity, unit_price, line_total, payment_type, fulfillment_type. Transaction includes id, status, payment_method, payment_mode, transaction_type, total, currency, created_at. license_ids carries the IDs of any fct_licenses rows tied to this order (use diviops_fc_license_get to fetch each row's redacted shape). Does NOT expose payment credentials, gateway secrets, or full license keys. Error codes: invalid_input (HTTP 400) when id is not a positive integer; not_found (HTTP 404) when no order matches; fluentcart.module_inactive (HTTP 412); fluentcart.query_failed (HTTP 500).",
3665
+ inputSchema: {
3666
+ id: z
3667
+ .number()
3668
+ .int()
3669
+ .positive()
3670
+ .describe("FluentCart order ID (fct_orders.id; for the local smoke runbook this is the order receipt anchor)."),
3671
+ },
3672
+ annotations: { idempotentHint: true },
3673
+ _meta: { idempotent: "true" },
3674
+ }, async ({ id }) => {
3675
+ const result = await wp.requestEnveloped(`/pro/fluentcart/orders/${id}`, { method: "POST" });
3676
+ return {
3677
+ content: [
3678
+ {
3679
+ type: "text",
3680
+ text: serializeEnvelope(result, "diviops_fc_order_get"),
3681
+ },
3682
+ ],
3683
+ };
3684
+ }, { target: "fluentcart", capabilityKey: "fluentcart_order_get" });
3685
+ // diviops_fc_order_mark_paid — POST /diviops/v1/pro/fluentcart/orders/{id}/mark-paid
3686
+ registerProTool("diviops_fc_order_mark_paid", {
3687
+ description: "Guarded local/offline mark-paid for a FluentCart order (Pro tier; V3.1; requires FluentCart installed + activated). Mirrors the canonical `FluentCart\\App\\Http\\Controllers\\OrderController::markAsPaid` sequence — updates the pending offline transaction, flips payment_status to 'paid' (unless partially_refunded), flips order status to 'processing' (and 'completed' for digital fulfillment), then dispatches `OrderPaid` (which fires the `fluent_cart/order_paid` WordPress action, the listener `LicenseGenerationHandler::maybeGenerateLicensesOnPurchaseSuccess` hangs off of) plus `OrderStatusUpdated`. Does NOT directly insert license rows — license generation is a side effect of the dispatched event, exactly like the FCP admin path. Refuses non-offline gateways (anything other than `payment_method='offline_payment'`) with `fluentcart.unsupported_payment_method` (HTTP 422) — this slice is for local/test/COD smokes only. Refuses canceled orders with `fluentcart.order_canceled` (HTTP 422). Already-paid orders are repeat-safe: returns `ok:true` with `data.already_paid: true` (no second event fires). **dry_run defaults to TRUE** for safety; apply requires `dry_run:false` PLUS `confirm_order_id` + `confirm_payment_method` + `confirm_due_amount` matching current state. Dry-run payload: { dry_run:true, plan: { summary, changes[] (payment_status, status, total_paid, transaction), warnings[] }, events: ['fluent_cart/order_paid', 'fluent_cart/order_status_updated'], order_id, licenses_before }. Apply payload: { order: OrderSummary (post-mutation), transaction: TransactionSummary, events_fired: string[], licenses_before, licenses_after, licenses_created, license_ids: number[], licenses: LicenseRedactedSummary[] (no full keys). Error codes: invalid_input (400) when id/confirmation fields are wrong; not_found (404); fluentcart.order_canceled (422); fluentcart.unsupported_payment_method (422); fluentcart.module_inactive (412); fluentcart.command_failed (500). Idempotency: repeat-safe via already_paid sentinel." +
3688
+ " Pass dry_run: false plus the confirm_* fields to apply.",
3689
+ inputSchema: {
3690
+ id: z
3691
+ .number()
3692
+ .int()
3693
+ .positive()
3694
+ .describe("FluentCart order ID (fct_orders.id) to mark paid."),
3695
+ dry_run: z
3696
+ .boolean()
3697
+ .optional()
3698
+ .default(true)
3699
+ .describe("When true (default), return the change plan without mutating state. Apply requires explicit dry_run: false + the confirm_* fields."),
3700
+ confirm_order_id: z
3701
+ .number()
3702
+ .int()
3703
+ .positive()
3704
+ .optional()
3705
+ .describe("Apply-mode confirmation: must equal `id`. Prevents accidentally marking the wrong order paid."),
3706
+ confirm_payment_method: z
3707
+ .string()
3708
+ .optional()
3709
+ .describe("Apply-mode confirmation: must equal the order's current payment_method (e.g. 'offline_payment')."),
3710
+ confirm_due_amount: z
3711
+ .number()
3712
+ .int()
3713
+ .min(0)
3714
+ .optional()
3715
+ .describe("Apply-mode confirmation: must equal (total_amount - total_paid) in stored units (cents). Inspect via diviops_fc_order_get."),
3716
+ mark_paid_note: z
3717
+ .string()
3718
+ .optional()
3719
+ .describe("Optional sanitize_text_field note to attach to the order's `note` column. Mirrors the admin mark-paid form field."),
3720
+ },
3721
+ annotations: { idempotentHint: false },
3722
+ _meta: { idempotent: "conditional" },
3723
+ }, async ({ id, dry_run, confirm_order_id, confirm_payment_method, confirm_due_amount, mark_paid_note, }) => {
3724
+ const body = {};
3725
+ // dry_run defaults to true at the MCP level — pass through whatever
3726
+ // the caller sent so the plugin can apply the same default.
3727
+ if (dry_run !== undefined)
3728
+ body.dry_run = dry_run;
3729
+ else
3730
+ body.dry_run = true;
3731
+ if (confirm_order_id !== undefined)
3732
+ body.confirm_order_id = confirm_order_id;
3733
+ if (confirm_payment_method !== undefined)
3734
+ body.confirm_payment_method = confirm_payment_method;
3735
+ if (confirm_due_amount !== undefined)
3736
+ body.confirm_due_amount = confirm_due_amount;
3737
+ if (mark_paid_note !== undefined)
3738
+ body.mark_paid_note = mark_paid_note;
3739
+ const result = await wp.requestEnveloped(`/pro/fluentcart/orders/${id}/mark-paid`, { method: "POST", body });
3740
+ return {
3741
+ content: [
3742
+ {
3743
+ type: "text",
3744
+ text: serializeEnvelope(result, "diviops_fc_order_mark_paid"),
3745
+ },
3746
+ ],
3747
+ };
3748
+ }, { target: "fluentcart", capabilityKey: "fluentcart_order_mark_paid" });
3749
+ // diviops_fc_license_list — POST /diviops/v1/pro/fluentcart/licenses
3750
+ registerProTool("diviops_fc_license_list", {
3751
+ description: "List FluentCart Pro licenses (Pro tier; V3.1; requires FluentCart Pro + Licensing module). Read-only. Filterable by product_id, variation_id, order_id, customer_id, status (active/inactive/disabled/expired/in_trial). License keys are NEVER returned in full — every row carries `redacted_key` only (first 4 + last 4 with ellipsis); use diviops_fc_license_get with explicit secret-handling opt-in if a full key is required for an authorized integration test. Returns the standardized envelope { ok, data?, error: { code, message, hint? } }; the success payload is { licenses: LicenseSummary[], pagination: { page, per_page, total, total_pages }, filters: { ... } }. LicenseSummary includes id, status, product_id, variation_id, order_id, customer_id, subscription_id, limit (0 = unlimited per License::getActivationLimit), activation_count, expiration_date, created_at, updated_at, redacted_key. Error codes: invalid_input (HTTP 400) when status is out of range; fluentcart.module_inactive (HTTP 412); fluentcart.licensing_unavailable (HTTP 412) when FluentCart Pro's Licensing module is absent; fluentcart.query_failed (HTTP 500).",
3752
+ inputSchema: {
3753
+ page: z
3754
+ .number()
3755
+ .int()
3756
+ .positive()
3757
+ .optional()
3758
+ .default(1)
3759
+ .describe("Page number, 1-indexed. Default 1."),
3760
+ per_page: z
3761
+ .number()
3762
+ .int()
3763
+ .positive()
3764
+ .optional()
3765
+ .default(20)
3766
+ .describe("Page size. Default 20, clamped to a max of 100."),
3767
+ product_id: z
3768
+ .number()
3769
+ .int()
3770
+ .positive()
3771
+ .optional()
3772
+ .describe("Filter to licenses for this FluentCart product ID."),
3773
+ variation_id: z
3774
+ .number()
3775
+ .int()
3776
+ .positive()
3777
+ .optional()
3778
+ .describe("Filter to licenses for this FluentCart variation ID."),
3779
+ order_id: z
3780
+ .number()
3781
+ .int()
3782
+ .positive()
3783
+ .optional()
3784
+ .describe("Filter to licenses issued by this order ID."),
3785
+ customer_id: z
3786
+ .number()
3787
+ .int()
3788
+ .positive()
3789
+ .optional()
3790
+ .describe("Filter to licenses owned by this customer ID."),
3791
+ status: z
3792
+ .enum(["active", "inactive", "disabled", "expired", "in_trial"])
3793
+ .optional()
3794
+ .describe("License status (fct_licenses.status). 'active' is the healthy default."),
3795
+ },
3796
+ annotations: { idempotentHint: true },
3797
+ _meta: { idempotent: "true" },
3798
+ }, async ({ page, per_page, product_id, variation_id, order_id, customer_id, status, }) => {
3799
+ const body = {};
3800
+ if (page !== undefined)
3801
+ body.page = page;
3802
+ if (per_page !== undefined)
3803
+ body.per_page = per_page;
3804
+ if (product_id !== undefined)
3805
+ body.product_id = product_id;
3806
+ if (variation_id !== undefined)
3807
+ body.variation_id = variation_id;
3808
+ if (order_id !== undefined)
3809
+ body.order_id = order_id;
3810
+ if (customer_id !== undefined)
3811
+ body.customer_id = customer_id;
3812
+ if (status !== undefined)
3813
+ body.status = status;
3814
+ const result = await wp.requestEnveloped("/pro/fluentcart/licenses", {
3815
+ method: "POST",
3816
+ body,
3817
+ });
3818
+ return {
3819
+ content: [
3820
+ {
3821
+ type: "text",
3822
+ text: serializeEnvelope(result, "diviops_fc_license_list"),
3823
+ },
3824
+ ],
3825
+ };
3826
+ }, { target: "fluentcart", capabilityKey: "fluentcart_license_list" });
3827
+ // diviops_fc_license_get — POST /diviops/v1/pro/fluentcart/licenses/{id}
3828
+ registerProTool("diviops_fc_license_get", {
3829
+ description: "Fetch a single FluentCart Pro license by ID (Pro tier; V3.1; requires FluentCart Pro + Licensing module). Read-only by default. The default response redacts the license key to `redacted_key` only (first 4 + last 4 with ellipsis). To surface the full key, pass BOTH `include_license_key: true` AND `confirm_secret_handling: true` — the response then carries `license.license_key` plus `_meta.contains_secret: true` naming the secret field. **Full license keys must never be pasted into PRs, issues, Slack, or any external surface.** Returns the standardized envelope { ok, data?, error: { code, message, hint? } }. Success payload: { license: { id, status, product_id, variation_id, order_id, customer_id, subscription_id, limit, activation_count, expiration_date, created_at, updated_at, redacted_key, license_key? } }. limit semantics: 0 = unlimited per License::getActivationLimit; positive integers are the actual activation cap. Error codes: invalid_input (HTTP 400) when id is non-positive or include_license_key was passed without confirm_secret_handling; not_found (HTTP 404); fluentcart.module_inactive (HTTP 412); fluentcart.licensing_unavailable (HTTP 412); fluentcart.query_failed (HTTP 500).",
3830
+ inputSchema: {
3831
+ id: z
3832
+ .number()
3833
+ .int()
3834
+ .positive()
3835
+ .describe("FluentCart license ID (fct_licenses.id)."),
3836
+ include_license_key: z
3837
+ .boolean()
3838
+ .optional()
3839
+ .describe("Opt-in: include the full unredacted license key in the response. Requires confirm_secret_handling: true. Default: redacted-only."),
3840
+ confirm_secret_handling: z
3841
+ .boolean()
3842
+ .optional()
3843
+ .describe("Required alongside include_license_key: true. Acknowledges that full license keys must not be pasted into reports, PRs, issues, or external chat surfaces."),
3844
+ },
3845
+ annotations: { idempotentHint: true },
3846
+ _meta: { idempotent: "true" },
3847
+ }, async ({ id, include_license_key, confirm_secret_handling, }) => {
3848
+ const body = {};
3849
+ if (include_license_key !== undefined)
3850
+ body.include_license_key = include_license_key;
3851
+ if (confirm_secret_handling !== undefined)
3852
+ body.confirm_secret_handling = confirm_secret_handling;
3853
+ const result = await wp.requestEnveloped(`/pro/fluentcart/licenses/${id}`, { method: "POST", body });
3854
+ return {
3855
+ content: [
3856
+ {
3857
+ type: "text",
3858
+ text: serializeEnvelope(result, "diviops_fc_license_get"),
3859
+ },
3860
+ ],
3861
+ };
3862
+ }, { target: "fluentcart", capabilityKey: "fluentcart_license_get" });
3863
+ // diviops_fc_license_activations_list — POST /diviops/v1/pro/fluentcart/licenses/{id}/activations
3864
+ registerProTool("diviops_fc_license_activations_list", {
3865
+ description: "List a FluentCart license's activation rows (Pro tier; V3.1; requires FluentCart Pro + Licensing module). Read-only. Returns one row per `fct_license_activations` entry for the license, including the joined `fct_license_sites.site_url` (the NORMALIZED form — scheme + trailing slash + `www.` prefix stripped per LicenseHelper::sanitizeSiteUrl — not the raw URL the consumer submitted). Filterable by status (active/inactive/deactivated). License keys are NEVER returned by this endpoint. Returns the standardized envelope { ok, data?, error: { code, message, hint? } }; success payload: { license_id, activations: Activation[], count, filters: { status } }. Activation row: id, license_id, site_id, site_url, status, is_local, product_id, variation_id, activation_method, last_update_date, last_update_version, created_at, updated_at. Error codes: invalid_input (HTTP 400); not_found (HTTP 404) when the license doesn't exist; fluentcart.module_inactive (HTTP 412); fluentcart.licensing_unavailable (HTTP 412); fluentcart.query_failed (HTTP 500). Useful for smoke verification (count + site_url presence) and for the activation-cap test described in the diviops-fluentcart skill.",
3866
+ inputSchema: {
3867
+ license_id: z
3868
+ .number()
3869
+ .int()
3870
+ .positive()
3871
+ .describe("FluentCart license ID whose activation rows to list (fct_licenses.id)."),
3872
+ status: z
3873
+ .enum(["active", "inactive", "deactivated"])
3874
+ .optional()
3875
+ .describe("Activation row status filter (fct_license_activations.status)."),
3876
+ },
3877
+ annotations: { idempotentHint: true },
3878
+ _meta: { idempotent: "true" },
3879
+ }, async ({ license_id, status, }) => {
3880
+ const body = {};
3881
+ if (status !== undefined)
3882
+ body.status = status;
3883
+ const result = await wp.requestEnveloped(`/pro/fluentcart/licenses/${license_id}/activations`, { method: "POST", body });
3884
+ return {
3885
+ content: [
3886
+ {
3887
+ type: "text",
3888
+ text: serializeEnvelope(result, "diviops_fc_license_activations_list"),
3889
+ },
3890
+ ],
3891
+ };
3892
+ }, {
3893
+ target: "fluentcart",
3894
+ capabilityKey: "fluentcart_license_activations_list",
3895
+ });
3896
+ // ── V3.2 — checkout readiness / status ────────────────────────────
3897
+ //
3898
+ // Pre-check surface so smoke runners can answer "is this site ready
3899
+ // to checkout?" without admin UI, raw SQL, or eval-file probes. All
3900
+ // three tools are read-only.
3901
+ // diviops_fc_status — POST /diviops/v1/pro/fluentcart/status
3902
+ registerProTool("diviops_fc_status", {
3903
+ description: "Inspect FluentCart checkout/license readiness (Pro tier; V3.2). Read-only. Returns a structured snapshot covering: site (wp_url, host, is_local_hint, wp_version), plugins (fluent_cart, fluent_cart_pro, diviops_agent_pro — { active, version } each), store (order_mode, currency, currency_symbol, currency_position, store_country, store_state, checkout/cart/receipt/shop page IDs), modules (license.active, stock_management.active+advanced_inventory), tables (core / licensing / advanced_inventory groups with count/expected/ok and gated_by hints — `ok: null` means the group is optional and module is off), gateways (registered_count, enabled_count, enabled_methods, registered_methods, local_safe_methods — slug-only summary; use diviops_fc_gateway_list/get for details), counts (orders, licenses, license_activations), and readiness (verdict: green|yellow|red, checkout_writable, license_writable, local_no_money_smoke_ready, public_launch_ready, warnings[], blockers[]). Distinguishes \"fluentcart inactive\" from \"core schema missing\" from \"license module on but tables missing\" — the same gaps that drove earlier local checkout/license smokes into WP-CLI and eval-file probes. No secrets are returned at any layer; gateway settings are summarized through diviops_fc_gateway_* tools. Pass include_table_lists=true to receive the full present[]/missing[] table arrays (default: count/expected/ok only, to keep the response compact). Returns the standardized envelope { ok, data?, error: { code, message, hint? } }. Error codes: fluentcart.module_inactive (412); fluentcart.command_failed (500).",
3904
+ inputSchema: {
3905
+ include_table_lists: z
3906
+ .boolean()
3907
+ .optional()
3908
+ .default(false)
3909
+ .describe("When true, include the full present[] and missing[] arrays in each `tables.*` group. Default false to keep the response compact."),
3910
+ },
3911
+ annotations: { idempotentHint: true },
3912
+ _meta: { idempotent: "true" },
3913
+ }, async ({ include_table_lists, }) => {
3914
+ const body = {};
3915
+ if (include_table_lists !== undefined)
3916
+ body.include_table_lists = include_table_lists;
3917
+ const result = await wp.requestEnveloped("/pro/fluentcart/status", { method: "POST", body });
3918
+ return {
3919
+ content: [
3920
+ {
3921
+ type: "text",
3922
+ text: serializeEnvelope(result, "diviops_fc_status"),
3923
+ },
3924
+ ],
3925
+ };
3926
+ }, { target: "fluentcart", capabilityKey: "fluentcart_status_get" });
3927
+ // diviops_fc_gateway_list — POST /diviops/v1/pro/fluentcart/gateways
3928
+ registerProTool("diviops_fc_gateway_list", {
3929
+ description: "List FluentCart registered payment gateways with secrets redacted (Pro tier; V3.2). Read-only. Returns one row per gateway registered with FluentCart's GatewayManager — each row carries `method`, `title`, `admin_title`, `label`, `route`, `enabled`, `upcoming`, `requires_pro`, `no_real_money` (true for offline/COD), `local_safe`, `webhook_required` (capability hint — reachability is NOT probed), `credentials_configured` (best-effort boolean inferred without reading credential values), `supports.subscriptions`, `supports.refund`, `settings_summary` (allowlisted non-secret fields only — `is_active`, `payment_mode`, `checkout_mode`, `checkout_label`, `checkout_logo`, `checkout_instructions`, `provider`, `define_*_keys` boolean flags, `*_is_encrypted` flags), `settings_source` (e.g. `fct_meta:fluent_cart_payment_settings_stripe`), `description`, `brand_color`, and a sanitized `meta` bag. NEVER returns: API keys, secret keys, publishable keys, webhook signing secrets, client IDs, customer IDs, vendor/seller IDs, tokens, or any value matching the credential-substring heuristic. Returns the standardized envelope { ok, data?, error: { code, message, hint? } }; success payload: { gateways: GatewayRow[], count }. Error codes: fluentcart.module_inactive (412); fluentcart.command_failed (500). Use as the discovery call before diviops_fc_gateway_get to inspect a specific method.",
3930
+ inputSchema: {},
3931
+ annotations: { idempotentHint: true },
3932
+ _meta: { idempotent: "true" },
3933
+ }, async () => {
3934
+ const result = await wp.requestEnveloped("/pro/fluentcart/gateways", { method: "POST" });
3935
+ return {
3936
+ content: [
3937
+ {
3938
+ type: "text",
3939
+ text: serializeEnvelope(result, "diviops_fc_gateway_list"),
3940
+ },
3941
+ ],
3942
+ };
3943
+ }, { target: "fluentcart", capabilityKey: "fluentcart_gateway_list" });
3944
+ // diviops_fc_gateway_get — POST /diviops/v1/pro/fluentcart/gateways/{method}
3945
+ registerProTool("diviops_fc_gateway_get", {
3946
+ description: "Fetch a single FluentCart payment gateway by method slug with secrets redacted (Pro tier; V3.2). Read-only. Same row shape as diviops_fc_gateway_list plus a `field_metadata` array describing what settings the gateway accepts (key + label + type + description from the gateway's `fields()` map) WITHOUT any stored values. `method` is one of the registered slugs from diviops_fc_gateway_list — common values: `offline_payment` (COD, local-safe, no webhook, no real money — the slug to use for local smoke runs), `stripe`, `paypal`, `paystack`, `airwallex`, `paddle` (Pro), `mollie` (Pro), `authorize_net` (Pro). NEVER returns credential values; field_metadata is metadata-only. Returns the standardized envelope { ok, data?, error: { code, message, hint? } }; success payload: { gateway: GatewayRow }. Error codes: invalid_input (HTTP 400) when method is empty; not_found (HTTP 404) when the slug is not registered (hint: \"Call diviops_fc_gateway_list to see the registered method slugs.\"); fluentcart.module_inactive (412); fluentcart.command_failed (500). Useful for confirming the local-safe gateway is enabled before a smoke run, and for surfacing whether Paddle / Stripe / etc. is registered without exposing keys.",
3947
+ inputSchema: {
3948
+ method: z
3949
+ .string()
3950
+ .min(1)
3951
+ .describe("Gateway method slug — e.g. `offline_payment`, `stripe`, `paypal`, `paddle`, `mollie`. Run diviops_fc_gateway_list first to enumerate registered slugs."),
3952
+ },
3953
+ annotations: { idempotentHint: true },
3954
+ _meta: { idempotent: "true" },
3955
+ }, async ({ method }) => {
3956
+ const safe = encodeURIComponent(method);
3957
+ const result = await wp.requestEnveloped(`/pro/fluentcart/gateways/${safe}`, { method: "POST" });
3958
+ return {
3959
+ content: [
3960
+ {
3961
+ type: "text",
3962
+ text: serializeEnvelope(result, "diviops_fc_gateway_get"),
3963
+ },
3964
+ ],
3965
+ };
3966
+ }, { target: "fluentcart", capabilityKey: "fluentcart_gateway_get" });
3562
3967
  }
3563
3968
  // ── Start ────────────────────────────────────────────────────────────
3564
3969
  async function main() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diviops/mcp-server",
3
- "version": "1.5.19",
3
+ "version": "1.5.21",
4
4
  "description": "MCP server exposing Divi 5 Visual Builder as tools for Claude",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",