Changelog
Every version, every fix, every new tool. The full paper trail.
What I changed, when, and why you should care.
July 2026
v2.2.0
MCP TypeScript SDK v2 is here. I upgraded the server, the wire, and every piece of evidence that stops “works on my machine” from becoming a release strategy.
- SDK v2, properly — I pinned every official TypeScript SDK package to
2.0.0and added a live registry gate that fails when “latest” moves. Dependency drift has lost its improv licence. - Two protocol eras, one server — I give modern clients the 2026 protocol,
server/discover, cache hints, and the complete wire contract; I keep a deliberate compatibility path for existing 2025 clients instead of launching them into the sun for dramatic effect. - Evidence from real handshakes — I made client certification record the protocol actually negotiated over HTTP and STDIO. I verify the exact candidate through Inspector, Claude Code, Docker, proxy, archive, and soak lanes rather than testing a suspiciously similar cousin.
- The v1 ghosts are gone — I removed stale helpers and active leftovers, then added a scanner so they cannot wander back in wearing a new filename.
I passed the official conformance suite across both supported protocol versions, then ran a six-minute candidate soak without errors, leaks, or interpretive networking. New protocol. Same store. Considerably less wishful thinking.
v2.1.0
I added FluentCart 1.6+ subscription support, minus the traditional rush to wire every shiny endpoint to a chat box and call it innovation.
Renewals are first-class reads
I added fluentcart_renewal_list and fluentcart_renewal_get so an assistant can list renewal
records and open one renewal in detail. I also retained the useful FluentCart 1.6 context in the
subscription projection, including collection method and pending-skip state when the store returns
it.
Subscription detail changes are guarded
I added a narrowly reviewed bill_times update to reversible mode for changing a subscription's
total billing-cycle limit. I check fresh bill_times and bill_count as a best-effort preflight,
reject an already stale or end-of-term change, and read the record back before reporting success.
FluentCart 1.6 has no atomic version precondition, so an ambiguous failure says the mutation may
have applied and requires a fresh read instead of a blind retry. I allow this only for store-billed
manual or system subscriptions; I fail closed before writing to gateway-billed automatic
subscriptions. I also fail closed when a subscription has linked FluentCart Pro licences because
the upstream update event can change licence state. I did not build a back door to billing control:
charges, renewal creation, skipping, voiding, resending, transaction synchronisation, refunds, and
subscription cancellation remain absent.
I left pause, resume, and reactivate absent too. FluentCart 1.6 can change scheduled charges and related renewal state through those lifecycle routes, and I cannot prove a supported restoration. An endpoint existing is not the same thing as an undo button. Remarkable, but useful.
I also removed fluentcart_subscription_fetch. I previously classed it as a read, but
FluentCart 1.6 uses it to resynchronise with the payment gateway and can mutate local subscription
state. I am leaving that in the admin, not behind a deceptively calm tool description.
I also made the generic store-settings write reject subscription_management_mode and
subscription_system_charge. They are store-wide billing controls, so I require a dedicated guard
instead of letting them slip through a generic settings form in a fake moustache.
FluentCart 1.6 compatibility refresh
I verified 2.1.0 with WordPress 7.0.2, FluentCart Core 1.6.0, and FluentCart Pro 1.6.0. “FluentCart 1.6+” identifies the compatibility work in this release; I am not claiming that I tested every future FluentCart version. I also removed the legacy tax-country delete operation from route discovery: it was a nonfunctional ghost route, not a missing feature to mourn.
v2.0.1
I made this the client-first release. Pick the app you already use, follow its exact recipe, and verify one store read. Revolutionary stuff, assuming one ignores the decade software spent making setup pages start with architecture diagrams.
Eight clear connection paths
I put ChatGPT Desktop, Codex CLI, Codex IDE extension, Claude Desktop, Cursor, VS Code with GitHub Copilot, Windsurf, and ChatGPT web together in one chooser. I gave each its current configuration surface and a useful verification step.
Claude Desktop supplies Node
I stopped asking Claude Desktop users to install Node.js for the MCPB extension. Claude Desktop supplies the runtime; I package FluentCart MCP's JavaScript and production dependencies, not a secret Node executable wearing a trench coat.
No global npm install
I corrected the local recipes: npx -y fluentcart-mcp downloads the package on demand and reuses
the package-manager cache. I do not install FluentCart MCP globally.
OpenAI desktop and web are no longer muddled together
I documented that ChatGPT Desktop shares ~/.codex/config.toml with Codex CLI and the Codex IDE extension, with
Settings → MCP servers → Add server as the shortest route. ChatGPT web uses OpenAI Secure MCP
Tunnel, separate Platform and Developer mode permissions, and an outbound-only private connection.
I no longer pass the FluentCart HTTP bearer key off as ChatGPT authentication.
The public ChatGPT Plugins Directory still requires FluentCart authorisation. A patch release may improve onboarding; I cannot acquire somebody else's trademark permission by sheer optimism.
v2.0.0
I made this a breaking release, and unapologetically so. Version 1.x handed an AI assistant the whole store on connection and trusted everyone involved to be careful. I started 2.0.0 from the opposite assumption.
Upgrading from 1.x will change what your AI can do
Writes are off unless you switch them on, the default mode presents three compact read tools instead of the entire catalogue, and refunds, cancellations, deletions and bulk actions are not exposed at all. Enabling reversible writes adds one executor. If your workflow depended on removed actions, it will stop working.
Writes are absent by default
I added FLUENTCART_WRITE_MODE and made it default to disabled. Its values are disabled and
reversible.
I filter before registration, not at call time, which is the part that matters: a tool your policy excludes cannot be listed, searched, described or invoked by name. I left no "the model found it anyway" path, because there is nothing to find.
I use disabled to expose reads. I use reversible to add work that can be undone — creating and updating
products, variants, coupons, customers, shipping rules, tax rules, attributes, order bumps and
upgrade paths.
I make deletion, bulk operations, order status changes, marking an order paid, and dispute handling unavailable in every mode. FluentCart provides no supported way to reverse any of them, so I do not classify any as a reversible write. I made this narrower than 1.x on purpose.
Refunding and cancellation are not available
I did not ship fluentcart_order_refund or fluentcart_subscription_cancel as product tools in
2.0.0. I provide no write mode or environment variable that enables them. Use FluentCart's admin
or official operator tooling for those jobs.
Modes: static is gone, dynamic is the default
I renamed what 1.x called static to full. I offer dynamic, curated, code, and full, and
I give you dynamic when you say nothing.
dynamic— three read tools:fluentcart_search_tools,fluentcart_describe_tools, andfluentcart_execute_read_tool. Reversible mode addsfluentcart_execute_reversible_write.code— two tools over a QuickJS WebAssembly sandbox,fluentcart_search_apiandfluentcart_execute_code. Read-only by construction: the sandbox has no route to a write.curated— a shortlist for the workflows people actually run.full— everything exposed, registered up front, exactly asstaticused to behave.
I now enforce definition-token budgets rather than merely reporting them: 1,500 for dynamic, 1,200
for code, and 12,000 for curated. I measure full mode on every build but never gate it, because
gating it would be gating the thing it exists to provide. I generate the figures for each revision
with node scripts/measure-tool-context.mjs, serialise them with mcp-tools-list-v1, count them
with gpt-tokenizer@3.4.0, and record them in release-contract.json.
HTTP is loopback-first
I bind the HTTP transport to 127.0.0.1 by default. I refuse a non-loopback bind without
FLUENTCART_MCP_API_KEY of at least 32 characters, and I run the check before listen() so a
misconfigured deployment fails immediately instead of quietly serving a store-administration API
to the internet.
I return exactly {"error":"Unauthorized"} for every authentication rejection and use a
constant-time comparison behind it. I make a wrong key indistinguishable from a missing one.
The 1.x documentation warned that without a key "anyone who discovers your endpoint can query your store". I made that untrue and removed the warning instead of leaving it there to look prudent.
Oversized responses fail loudly
I now return a RESPONSE_TOO_LARGE error when a response exceeds the budget and name the remedies:
narrow the page size, date range, or field list. I previously returned a truncated payload marked
_truncated, which is a polite way of describing data that looks complete and isn't.
Least-privilege roles are supported
I make the server do what the configured WordPress user can do, and nothing more. I corrected the old advice to hand over a full admin login: I support a narrow FluentCart role, and I recommend pairing one with the default read-only write mode.
Support profile
I verified this release against WordPress 7.0.2 with FluentCart 1.5.5 and FluentCart Pro 1.5.4.
I also captured the route surface of an older 1.3.9 install from a real runtime, but support is not claimed by that route evidence. It records which endpoints the build serves; I do not claim that these tools work against it, and I do not claim support until a tool-level compatibility check passes. I treat a smaller tool list on an older store as evidence that route pruning works, not that I exercised the surviving tools there.
Also
- I no longer expose subscription pause, resume, or reactivate as tools.
- I no longer include the customer address dropdown helper or the duplicate address-add tool.
- I make MCP Resources read through to your store on every read; I keep no resource-layer cache, and I no longer imply otherwise in the documentation.
- I generate published counts from the release contract instead of retyping one number across six files, which is how I let them drift in the first place.
March 2026
v1.1.0
Released 4 March 2026 — npm · GitHub Release
I made this the "full audit" release. I tested every tool against live FluentCart 1.3.13, read the backend PHP source line by line, and hunted down every schema, response shape, and undocumented quirk. I shipped 41 bug fixes and 30 quality improvements, and filed 14 new upstream issues. I dropped the tool count from 279 to 274 because five backend endpoints are genuinely broken with no workaround.
274 total tools (was 279).
What changed
Response transforms — I made list endpoints return trimmed payloads. I added product_type and
fulfillment_type to product_list, and purchase_value, ltv, and purchase_count to
customer_list. I strip subscription bloat with transformSubscription() and trim variants to
essential fields with trimVariant().
Fetch-merge pattern — I made write tools fetch current state, merge your changes, and send the
complete object instead of silently nuking unspecified fields. I applied the pattern to
coupon_update, customer_update, variant_update, product_update_detail, and
settings_save_store.
Cache invalidation — I made write tools declare which cache keys to invalidate through
invalidates in the factory. I wired 17 write tools across tax classes, tax settings, shipping
zones, shipping classes, and roles. No more stale cached data after mutations.
Schema corrections from PHP source — I changed bundle_save from the wrong bundle_items to
bundle_child_ids. I corrected the invalid pricing_update enum values to simple_variations and
advanced_variations. I also added the missing action: 'euCrossBorderSettings' discriminator
and eu_vat_settings wrapper to tax_eu_vat_save.
Timing-safe auth — I replaced HTTP Bearer token string equality with crypto.timingSafeEqual.
Bug fixes (41)
| Area | Fix |
|---|---|
| Coupons | coupon_update rewritten with fetch-merge pattern |
| Coupons | coupon_list response transform added (trim to essential fields) |
| Coupons | coupon_apply/coupon_cancel converted to createTool with correct field names |
| Coupons | coupon_settings_save specific field schema added |
| Customers | customer_list transform includes purchase analytics fields |
| Customers | customer_update rewritten with fetch-merge pattern |
| Customers | customer_bulk_action enum restricted to delete_customers |
| Customers | customer_update_additional_info now handles label sync correctly |
| Subscriptions | subscription_list now supports active_view filter |
| Subscriptions | Response transforms strip bloat from subscription data |
| Products | product_list transform includes product_type and fulfillment_type |
| Products | product_bulk_action enum restricted to valid actions |
| Products | product_update_detail rewritten with fetch-merge pattern |
| Products | product_fetch_by_ids array serialisation fixed |
| Products | variant_create title marked as required |
| Products | variant_update warning added about subscription field re-specification |
| Products | buildVariantFromExisting now spreads other_info instead of mutating input |
| Products | bundle_save field name corrected to bundle_child_ids |
| Products | pricing_update enum values corrected to match backend constants |
| Products | Upgrade path schemas corrected |
| Products | integration_save integration field schema added |
| Products | feed_status field renamed to notification_id |
| Products | terms_by_parent annotations corrected (readOnly + idempotent) |
| Tax | tax_eu_vat_save rewritten as createTool with correct payload structure |
| Tax | Cache invalidation wired to tax write tools |
| Integrations | integration_change_feed_status enum added |
| Integrations | integration_get_feed_settings required fields added |
| Integrations | integration_save_feed_settings required fields + integration field added |
| Settings | settings_save_store rewritten to flat key-value structure |
| Shipping | Cache invalidation wired to all shipping write tools |
| Roles | Capability guards and FluentCart Pro requirement notes added |
| Roles | role_get WARNING (empty backend), role_update WARNING (no-op) |
| Files | file_upload WARNING (multipart-only, JSON not supported) |
| Files | file_bucket_list driver field marked required with warning |
email_template_preview param renamed to match backend | |
email_list format note added | |
| Orders | order_sync_statuses warning added |
| Orders | order_transaction_update_status warning added |
| Orders | Order bump tools get capability guards |
| Reports | Duplicate report clarifications added |
| Public | public_user_login HTTPS security warning added |
Infrastructure improvements (30 P2 fixes)
- I refactored
truncateResponsewith asliceToFithelper and a while-loop safety cap for high-variance arrays. - I made
server.tsimportDYNAMIC_TOOL_COUNTinstead of hardcoding3. - I extracted
parseSuccessBodyfromrequest()to reduce cognitive complexity. - I extracted
executeStatusOperationsfrom the order status handler. - I updated the Biome config to exclude test utility scripts from linting.
- I removed 22 stale debug and scenario test files: 6,813 lines of archaeology.
Upstream FluentCart bugs filed
14 new issues filed on fluent-cart/fluent-cart (issues #22–#35):
| Issue | Bug |
|---|---|
| #22 | revenue-by-group and fetch-order-by-group crash with SQL syntax error for all groupKey values |
| #23 | POST /coupons returns full WordPress HTML page instead of JSON |
| #24 | Customer address API — three endpoint inconsistencies |
| #25 | Subscription pause, resume, and reactivate return 'Not available yet' |
| #26 | Role get and update methods have empty bodies — endpoints are no-ops |
| #27 | Coupon apply expects 'coupon_code' — inconsistent with every other coupon endpoint |
| #28 | File upload endpoint requires multipart only — no JSON-based upload option |
| #29 | Customer bulk action only supports delete — no status updates |
| #30 | Email template preview crashes — undocumented 'template' parameter required |
| #31 | Transaction status update accepts any value — no server-side validation |
| #32 | Coupon cancel crashes with null dereference when no coupons applied |
| #33 | EU VAT settings endpoint requires undocumented action discriminator |
| #34 | File bucket-list throws 'Invalid driver' — unclear which driver values are accepted |
| #35 | Coupon re-apply and checkProductEligibility are checkout-only but registered as admin REST routes |
Previously filed (v1.0.1): #9–#15, #17–#21
v1.0.1
Released 3 March 2026 — npm · GitHub Release
I made this the "I actually tested it against a real store" release. I fixed everything v1.0.0 got wrong with product tools. I tested every tool live against FluentCart 1.3.13, hunted down the ones that failed, found their root causes, and either fixed them or filed upstream bugs.
New tools (3)
| Tool | What it does |
|---|---|
fluentcart_product_editor_mode_update | Switch product long description between wp-editor and block-editor |
fluentcart_product_create_dummy | Generate test products for development |
fluentcart_product_variant_option_update | Configure attribute combinations for advanced_variations products |
279 total tools (was 276).
Subscription pricing support
I closed the big feature gap in v1.0.0: you could read subscription data but could not write
it. I added the full subscription billing schema to product_pricing_update, variant_create, and
variant_update:
payment_type—onetimeorsubscriptionrepeat_interval—daily,weekly,monthly,quarterly,half_yearly,yearlytimes— billing cycle count (0 = unlimited)trial_days— trial period before first chargemanage_setup_fee/signup_fee/signup_fee_name— one-time setup feescompare_price— strike-through priceitem_status—active/inactiveper variantvariation_type—simple/variable/subscriptionon pricing update
Bug fixes
Price corruption — The centrepiece disaster of v1.0.0. I sent values that were already in cents to an API that expects currency units and multiplies by 100 internally. A 400 PLN product became 40,000 PLN. I now send currency units from every write tool and document cents on every read tool.
Cascading variant corruption — I made product_pricing_update fetch the full product state,
convert existing variant prices from cents back to currency units, and merge your changes before
sending. It no longer silently nukes every variant because you changed a product title.
product_create nested schema — I flattened detail: { fulfillment_type } to
fulfillment_type at schema level and moved the nesting into the handler, because MCP hosts turned
the original shape into unusable parameters.
variant_list always 500 — I now send explicit page and per_page values because
FluentCart's ProductVariationController::index() crashes when query parameters are null.
variant_create and variant_update broken — I rewrote both with proper createTool handlers
after MCP flattening exposed schema and handler mismatches. I also made variant_update
fetch-then-merge so it does not destroy fields you did not touch.
variant_fetch_by_ids empty results — I fixed incorrect array-parameter serialisation.
product_fetch_by_ids empty results — I fixed the same issue there too. Variety is overrated.
product_taxonomy_sync wrong param name — I changed term_ids to FluentCart's expected
terms. One letter away from working.
product_taxonomy_delete wrong param type — I replaced the term_ids array with the single
term integer expected by FluentCart's deleteTaxonomyTermFromProduct().
attribute_group_update fails without slug — I now fetch the current group to retain its slug
when you do not provide one, because FluentCart requires slug even for title-only changes.
product_list filter_type param removed — I removed the misleading filter_type parameter;
it controls FluentCart's internal filter mode, not product type. I documented all valid
active_view values instead: publish, draft, subscribable, not_subscribable, physical,
digital, bundle, and non_bundle.
post_content stripped from product GET — I stopped removing long descriptions. I now strip
only integrations and pricing_table.
Upstream FluentCart bugs filed
Confirmed against FluentCart 1.3.13, filed on fluent-cart/fluent-cart:
| Issue | Bug |
|---|---|
| #14 | Product duplication fails when variant SKU is empty string (unique constraint violation) |
| #15 | AttrTermResource::create() validates group_id against wrong table (fct_atts_terms instead of fct_atts_groups) |
Previously filed (v1.0.0): #9 pricing-table only updates description, #10 fetchVariationsByIds crashes, #11 GET /products/variants null params, #12 no lightweight product update endpoint, #13 price unit asymmetry
v1.0.0
Released 3 March 2026 — npm · GitHub Release
I made this the stable release and took the surface from 200 tools to 276. I added Docker support, HTTP transport, a Claude Desktop extension, caching, prompts, resources, dynamic mode—the works.
New tool modules (5)
| Module | Tools | What |
|---|---|---|
| Shipping | 15 | Zones, methods, classes — full CRUD with zone reordering |
| Tax | 22 | Classes, rates, EU VAT, country tax IDs, shipping overrides, records |
| Email Notifications | 8 | Templates, shortcodes, previews, global settings |
| Roles | 7 | Role CRUD, user lists, permission management |
| Files | 4 | Upload, list, delete, bucket management |
New tools in existing modules
| Module | New tools | Highlights |
|---|---|---|
| Reports (core) | +10 | Country heatmap, cart analysis, order value distribution, day/hour patterns, completion time, weeks-between-refund |
| Reports (insights) | +7 | License charts, retention analysis, subscription cohorts, retention snapshots |
| Settings | +7 | Module management, confirmation pages, payment method ordering, print templates |
Features
Docker support — I added a multi-stage Node 22 Alpine build and published it to Docker Hub
(vcodesh/fluentcart-mcp) and GHCR, with a one-line deployment for remote servers and ChatGPT
integration.
HTTP transport — I made --transport http start an Express server with /mcp (SSE) and
/health endpoints, plus optional Bearer token authentication through FLUENTCART_MCP_API_KEY. I
kept it stateless, so session affinity can remain somebody else's hobby.
Dynamic toolset mode — I made --mode dynamic register 3 meta-tools instead of 276. The AI
searches, discovers, and executes tools on demand, dropping the initial tool list from roughly 30K
tokens to 1.5K for MCP hosts with tool-count limits.
In-memory TTL cache — I added three tiers: SHORT (2 minutes), MEDIUM (10 minutes), and LONG (1 hour). I apply them to stable reference data so the AI does not repeat API calls when it asks the same question twice in one conversation.
MCP Prompts — I added 5 workflow prompts that structure common tasks:
analyze-store-performance, investigate-order, customer-overview, catalog-summary, subscription-health
MCP Resources — I added 4 read-only resources for store config, countries, payment methods, and
filter options through resource:// URIs.
Claude Desktop Extension — I attached an .mcpb bundle to every GitHub Release. Download,
double-click, enter credentials. No Node.js, terminal, or JSON.
Interactive setup wizard — I made npx fluentcart-mcp setup walk through URL, username, and
Application Password with live connection testing and automatic retry.
Response size limits — I capped responses at 80,000 characters with intelligent array slicing. No more MCP protocol overflows on large product catalogues.
Request tracing — I added an X-Request-Id UUID header to every API call.
Structured logging — I added MCP-native logging with debug, info, warn, and error levels.
Bug fixes and schema corrections
| Fix | Module |
|---|---|
Report date range params used bracket notation (params[startDate]) — changed to flat startDate | Reports |
Coupon create used wrong field names (name→title, value→amount, usage_limit→conditions.max_uses) | Coupons |
Customer address had first_name/last_name — changed to name (full name, required) | Customers |
Customer and order sort params used order_by/order_type — changed to sort_by/sort_type | Customers, Orders |
Label create used value — changed to title | Labels |
Generic Error thrown for all API failures — now typed FluentCartApiError with machine-readable codes | Client |
Response transforms
I made list and detail endpoints return slimmer responses to stay within context windows:
product_list— ID, title, status, slug, date onlyproduct_get— stripsintegrationsand variantpricing_tableorder_get— stripsactivities,post_content, transactionmetacustomer_list— core fields only (id, name, email, status, spend)customer_get— replaces full address array withaddress_countapp_init— stripstrans(translation strings)
Breaking changes from v0.9.0
If you built anything against v0.9.0 (you probably didn't — it was out for 12 hours):
- Coupon, customer address, label, and report tool schemas changed significantly
fluentcart_variant_updatenow requiresproduct_idalongsidevariant_id- Sort parameters standardised to
sort_by/sort_typeacross all listing tools structuredContentremoved from tool response type
v0.9.0
Released 2 March 2026 — Feature-complete pre-release.
I shipped 200 tools across 22 modules: the first version that actually did things. I covered everything from orders and subscriptions to product management and reports. I supported STDIO only, with no Docker, caching, resources, or prompts.
I included orders, order transactions, customers, coupons, subscriptions, products, product options, integrations, reports, settings, order bumps, activity, application, dashboard, labels, notes, miscellaneous, and public modules.
I wired the CI pipeline for typecheck, lint, test, build, smoke test, npm publishing, and GitHub Release.
v0.1.0
Released 2 March 2026 — I shipped the initial package structure, CLI skeleton, API client, and tool factory with zero functional tools. I left it on npm for approximately two hours before v0.9.0 replaced it. A distinguished career.