Skip to content

Guide the WooCommerce setup and flag keys refused again and again - #1634

Merged
helpcodeai merged 2 commits into
mainfrom
feat/woo-guided-setup
Nov 11, 2026
Merged

helpcodeai merged 2 commits into
mainfrom
feat/woo-guided-setup

Conversation

@helpcodeai

Copy link
Copy Markdown
Contributor

Summary

WooCommerce setups mostly fail before the key matters. 28 of the 41 WooCommerce connectors that never made a call were chat installs still holding {{WOOCOMMERCE_URL}}. Most failed checks were a wrong address, Plain permalinks or a Cloudflare challenge, and all of them got the same "No WooCommerce REST API" message. Separately, one connector answered 401 woocommerce_rest_cannot_view for three days, up to 145 times a day, and nothing on the dashboard showed it.

This PR covers items M4 (step 1 of the guided setup), M7 (repeated-401 signal) and the in-app part of M6 (no emails) from the WooCommerce plan. It builds on #1631.

Changes

  • adapters/woocommerce-setup.ts (new):
    • normalizeWooCommerceUrl: trims the value, sets https, and cuts /wp-json…, /wp-admin…, /wp-login.php, index.php, the query, the fragment and trailing slashes. A subfolder install is kept.
    • discoverWooCommerce: unauthenticated GETs through outboundRequest (same SSRF policy and per-redirect checks as the other setup checks; private IPs give no answer), with an 8 s timeout, asking only for ?_fields=home,namespaces. It works out:
      • the site's own address (home), returned as corrected;
      • whether wc/v3 is present (WooCommerce inactive, or older than 3.5);
      • Plain permalinks: no /wp-json/ but ?rest_route=/ answers. The text names Settings → Permalinks and links …/wp-admin/options-permalink.php;
      • a page URL: falls back to the host's root index;
      • a Cloudflare challenge, Wordfence, Sucuri, Solid Security, Imunify360, ModSecurity, or an unnamed 403/406/429/503 page;
      • a REST API limited to signed-in users;
      • DNS, invalid certificate, http only, connection refused, timeout.
      • DNS, TLS, http-only and Plain permalinks are final: the check stops before the key is sent.
    • It also tells WooCommerce's two 401s apart (woocommerce_rest_cannot_view vs woocommerce_rest_authentication_error).
  • NORMALISERS.woocommerce in env-var-checks.ts. It runs on import, verify, the variable editor and the chat install; normalizeAdapterEnvValue gets an optional slug for that.
  • Setup check (AdaptersService.checkCredentials): discovery runs before the probe. home replaces the typed address and comes back as corrected + note; the setup page already writes corrected into the field and saves it, so there is no frontend change. A failure gets the WooCommerce-specific text in place of the generic hint.
  • Import probe (runImportProbe): the same advice on failure.
  • Chat install (ConnectorSetupService.install, like checkOdooAddress):
    • uses home and reports it in checkedTheAddress;
    • refuses a host name that does not exist, so the assistant asks again;
    • passes on any problem the user must fix in WordPress first, with the fix link.
  • M7: connectors/engines/credential-refusal-tracker.ts + DynamicMcpTools.
    • Trigger: 5 consecutive HTTP 401s on read tools of a connector whose credential is stored on it (API_KEY, QUERY_AUTH, BEARER_TOKEN, BASIC_AUTH, OAUTH1, HMAC, WS_SECURITY). Not for a per-user sign-in, a credential built from the caller, OAuth2 or the login flow; those record their own refusals.
    • Effect: ConnectorAuthStatusService.mark(needs_reauth, code credential_refused) with the new { notify: false }, so no email. The incident counts as already told, so later marks send nothing either.
    • A status another service recorded is never overwritten.
    • The first working call clears it (one query per connector per process otherwise). Editing the connector clears it too, as before.
    • The existing dashboard badge (auth-status.tsx) shows it.
  • setup_get_status:
    • credential: { ok: false, since, message } + fixUrl + credentialHint for a ready connector marked needs_reauth;
    • M6: unfinishedSince + unfinishedHint for a catalog connector still needs_input 24 h after creation. The assistant offers the fresh finishSetupUrl (already returned for every unfinished connector) or asks whether to remove it.
  • error-hints.ts: WooCommerce 401 rule at call time (the same two texts), with the connector page link.

Type

  • Bug fix
  • New feature
  • Documentation
  • Refactoring
  • Performance improvement
  • Other (describe below)

Cloud vs self-hosted

The behaviour is the same in both modes. Only the firewall text differs:

  • Cloud: names EGRESS_IPS ("AnythingMCP Cloud's address 104.248.242.235" when set).
  • Self-hosted: names "the address of your AnythingMCP server", or "this AnythingMCP server's address(es) …" when EGRESS_IPS is set.

Discovery follows the instance's SSRF policy, so a self-hosted install that allows private addresses can check an intranet shop.

No Prisma change (M7 uses the existing auth_status* columns), no emails, nothing under ee/, no new endpoint or DTO (no new input). Every query keeps its organizationId scope.

Testing

  • Added/updated unit tests
  • Tested manually (describe steps below)
  • No tests needed (explain why)

Commands (worktree with node_modules symlinked to the main checkout; DATABASE_URL=postgresql://x:x@localhost:5433/x npx prisma generate first), all in packages/backend:

  • npm run lint: 0 errors, 11 warnings, all in files this PR does not touch. No files rewritten.
  • npx tsc --noEmit -p tsconfig.json: no errors from this change. The only errors are the local checkout's missing @types/picomatch, @aws-sdk/*, basic-ftp and ssh2 (present in CI).
  • npx jest --maxWorkers=4 on the touched and new suites (woocommerce-setup, adapters.service.woocommerce-verify, connector-setup.service, env-var-checks, error-hints*, credential-refusal-tracker, connector-auth-status.service, dynamic-mcp-tools.credential-refused): all pass. Locally I used a jest config that maps the missing SDK modules to a stub; CI has them.
  • Full backend suite with the same local config: 1075 suites passed, 2 failed for local reasons only:
    • auth/jwt-or-management-key.guard.spec.ts needs a live Postgres;
    • ee/file-search/sources/sources.spec.ts needs the real S3/FTP/SSH SDKs that were stubbed.

Manual: discoverWooCommerce against real public sites (read-only GETs, no keys), 11 Nov 2026:

Input Result
wpbeginner.com home https://www.wpbeginner.com, no wc/v3 → "WooCommerce is not active"
https://woocommerce.com/wp-admin/ WooCommerce found, address cleaned to https://woocommerce.com
http://woocommerce.com/products/woocommerce-subscriptions/ root fallback, corrected https://woocommerce.com
https://www.nalgene.com/shop/ redirect followed, corrected https://nalgene.com
https://www.porterandyork.com Cloudflare "Just a moment" → blocked, Cloud IP named
https://jetpack.com, https://example.com not_wordpress
https://no-such-shop-xyz-4711.com dns, final
https://expired.badssl.com tls (CERT_HAS_EXPIRED), final
https://192.168.1.10 nothing said (SSRF policy)

Also confirmed: GET https://woocommerce.com/wp-json/wc/v3/orders?per_page=1 without a key answers 401 woocommerce_rest_cannot_view. So that code also means the key was not seen at all, and the hint says so: the key's user lacks rights, or WordPress does not know it runs on https (WooCommerce then ignores keys in the query string).

Notes / open points

  • WOOCOMMERCE_URL pattern not tightened. The backend normaliser runs before every pattern check, so /wp-json and /wp-admin can no longer be stored. But the setup page checks the pattern in the browser before sending, without the normaliser. A tighter pattern would refuse https://shop.com/wp-admin/ with a manual "remove /wp-admin" fix instead of cleaning it silently, and would suggest cutting a subfolder install's folder. Tightening would need the same normaliser in frontend/src/lib/address-fix.ts.
  • Hard-coded IP in the adapter's 403 hint. Auto-apply catalog updates that only add optional query parameters; WooCommerce sync filters #1631's verifyHints["403"] in woocommerce.json still names 104.248.242.235. That text now only shows when discovery found nothing (discovery's own blocked text uses EGRESS_IPS). I did not touch woocommerce.json to stay clear of the compact-defaults PR.
  • M7 never blocks calls. It only records and shows the status. The key may start working again (for example after the user's role is restored), and the next working call clears it. Turning on the existing one-email-per-incident is a one-line change (drop { notify: false }), but that is customer contact, so it is left off.
  • M6 is in-app only. The model sees unfinishedSince only when it calls setup_get_status. Nothing pushes it into other tool answers or instructions; needsSetup in the connections list already carries the link.
  • No catalog doc change. docs/catalog/connectors/woocommerce.md is generated by the website repo.

Checklist

  • My code follows the existing code style
  • I have tested my changes locally
  • I have updated documentation (if applicable) — none in this repo (see notes)
  • All existing tests pass (npm test) — see Testing for the two suites that only fail locally
  • This PR has a descriptive title

Related Issues

Follows #1631. Items M4, M6 (in-app part) and M7 of the WooCommerce plan of 11 Nov 2026.

- WooCommerce address: cleaned silently (https, no /wp-admin, /wp-json,
  /wp-login.php, query or trailing slash), then asked without a key at
  /wp-json/: WordPress's own address is used and handed back as
  `corrected`; missing WooCommerce, a version below 3.5, Plain permalinks
  (with the Settings -> Permalinks link), Cloudflare and security-plugin
  blocks (naming EGRESS_IPS on Cloud, "the address of your AnythingMCP
  server" on self-hosted), DNS, certificate and http-only answers get
  their own text. Used by the setup check, the install probe, the chat
  install and setup_get_status.
- WooCommerce 401s told apart (woocommerce_rest_cannot_view vs an unknown
  key) in the setup check and at call time.
- Repeated 401s on read tools of a connector with a stored key are
  recorded as its credential status (dashboard, setup_get_status), without
  an email; a working call or an edit clears it.
- setup_get_status points out catalog connectors still waiting for their
  values after 24 hours and asks the assistant to offer the new link.
@helpcodeai
helpcodeai requested a review from D3nisty as a code owner November 11, 2026 10:47
@helpcodeai
helpcodeai merged commit eb502b0 into main Nov 11, 2026
16 checks passed
@helpcodeai
helpcodeai deleted the feat/woo-guided-setup branch November 11, 2026 11:26
@github-actions github-actions Bot locked and limited conversation to collaborators Nov 11, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant