@awesomate/hosting-mcp 0.88.2 → 0.88.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -41747,6 +41747,7 @@ var TITLES = {
41747
41747
  awesomate_elementor_mcp_connect: "Connect Elementor MCP",
41748
41748
  awesomate_app_create: "Create app",
41749
41749
  awesomate_app_get: "Get app details",
41750
+ awesomate_app_remove_failed: "Remove an app whose setup failed",
41750
41751
  awesomate_app_scaffold: "Get app scaffold guide",
41751
41752
  awesomate_app_deploy_info: "Get app deploy briefing",
41752
41753
  awesomate_app_update: "Link repo to app",
@@ -42048,11 +42049,31 @@ server.registerTool(
42048
42049
  }
42049
42050
  }
42050
42051
  );
42052
+ function withCredentialStaleWarning(data) {
42053
+ if (!data || typeof data !== "object") return data;
42054
+ const d = data;
42055
+ if (d.stale !== true) return data;
42056
+ const asOf = d.asOf ? new Date(d.asOf) : null;
42057
+ const when = asOf && !Number.isNaN(asOf.getTime()) ? `as of ${asOf.toLocaleDateString("en-AU", { timeZone: "Australia/Sydney" })} ${asOf.toLocaleTimeString("en-AU", { timeZone: "Australia/Sydney", hour: "2-digit", minute: "2-digit" })} Sydney time (${d.asOf})` : "with no known refresh time";
42058
+ const why = {
42059
+ consent: `the refresh needs the "How your automations are running" privacy setting${d.settingsUrl ? ` (${d.settingsUrl})` : ""}`,
42060
+ refresh_failed: "the hub could not refresh it from the n8n instance just now",
42061
+ n8n_unreachable: "the hub could not reach the n8n instance's database to check it"
42062
+ }[d.staleReason ?? "refresh_failed"];
42063
+ const parts = [`This credential list may be out of date: it is ${when}, because ${why}.`];
42064
+ const added = d.drift?.added ?? [];
42065
+ const removed = d.drift?.removed ?? [];
42066
+ if (added.length) parts.push(`n8n also has: ${added.map((c) => `${c.name} (${c.type}, ${c.credential_id})`).join(", ")}.`);
42067
+ if (removed.length) parts.push(`No longer in n8n: ${removed.join(", ")}.`);
42068
+ parts.push("Tell the user the list may be out of date before relying on it, and check the credential in n8n itself when it matters.");
42069
+ const { stale: _s, ...rest } = d;
42070
+ return { warning: parts.join(" "), stale: true, ...rest };
42071
+ }
42051
42072
  server.registerTool(
42052
42073
  "awesomate_n8n_inspect",
42053
42074
  {
42054
42075
  annotations: annotate("awesomate_n8n_inspect", READ_ONLY),
42055
- description: `Instance inventory reads, one tool: 'nodes' (distinct node types in use, counts, versions, community/AI flags), 'datatables' (tables + columns + row counts), 'datatable_rows' (pass datatableId; limit \u2264 100), 'possibilities' (facts for "what could I automate": connected services with live-usage cross-check, unused connections, top nodes, AI tools [null = unknown, not none], community packages, counts \u2014 YOU turn these into suggestions, grounded only in what's actually there), 'credentials' (names/types/inferred service \u2014 never secrets), 'variables' ($vars keys). All consent-gated server-side; 403 consent_required \u2192 settingsUrl.`,
42076
+ description: `Instance inventory reads, one tool: 'nodes' (distinct node types in use, counts, versions, community/AI flags), 'datatables' (tables + columns + row counts), 'datatable_rows' (pass datatableId; limit \u2264 100), 'possibilities' (facts for "what could I automate": connected services with live-usage cross-check, unused connections, top nodes, AI tools [null = unknown, not none], community packages, counts \u2014 YOU turn these into suggestions, grounded only in what's actually there), 'credentials' (names/types/inferred service \u2014 never secrets; a leading 'warning' means the list may be out of date: say so to the user), 'variables' ($vars keys). All consent-gated server-side; 403 consent_required \u2192 settingsUrl.`,
42056
42077
  inputSchema: {
42057
42078
  what: external_exports.enum(["nodes", "datatables", "datatable_rows", "possibilities", "credentials", "variables"]),
42058
42079
  datatableId: external_exports.string().optional().describe("datatable_rows only"),
@@ -42074,7 +42095,8 @@ server.registerTool(
42074
42095
  credentials: "/api/my-n8n/machine/credentials",
42075
42096
  variables: "/api/my-n8n/machine/variables"
42076
42097
  }[what];
42077
- return textResult(await hubGet(cfg, path));
42098
+ const data = await hubGet(cfg, path);
42099
+ return textResult(what === "credentials" ? withCredentialStaleWarning(data) : data);
42078
42100
  } catch (err) {
42079
42101
  return errorResult(err);
42080
42102
  }
@@ -42265,7 +42287,7 @@ server.registerTool(
42265
42287
  "awesomate_domain_add",
42266
42288
  {
42267
42289
  annotations: annotate("awesomate_domain_add", MUTATING),
42268
- description: "Add a custom domain to the user's hosting account. Returns the DNS steps they need to complete at their registrar. **Needs Support Plus or above from Claude** \u2014 Essentials gets 403 `upgrade_required` even though its plan DOES include one custom domain, because adding it from a machine token is Support Plus; tell them it takes two clicks at hub.awesomate.ai/sites/domains instead. Plan COUNT limits are enforced separately on top.",
42290
+ description: "Add a custom domain to the user's hosting account. Returns the DNS steps they need to complete at their registrar: `dnsInstructions` (A rows only when there is a public address; CNAME rows always) and `dnsNote`. When `dnsNote` is set, relay it word for word: on that server the records must be Proxied (orange cloud) in Cloudflare and SSL/TLS set to Full, not Flexible (Full (strict) only once HTTPS is on); a DNS-only record will not load. **Needs Support Plus or above from Claude** \u2014 Essentials gets 403 `upgrade_required` even though its plan DOES include one custom domain, because adding it from a machine token is Support Plus; tell them it takes two clicks at hub.awesomate.ai/sites/domains instead. Plan COUNT limits are enforced separately on top.",
42269
42291
  inputSchema: { domain: external_exports.string().describe("The domain to add, e.g. example.com") }
42270
42292
  },
42271
42293
  async ({ domain }) => {
@@ -42547,7 +42569,7 @@ server.registerTool(
42547
42569
  "awesomate_app_get",
42548
42570
  {
42549
42571
  annotations: annotate("awesomate_app_get", READ_ONLY),
42550
- description: "Get one app plus its environments (subdomains, ports, db engine/name, deploy + health state) and any hostnames of the user's own attached to them. Poll this after awesomate_app_create \u2014 status goes provisioning \u2192 active | failed (read provision_error on failure).",
42572
+ description: "Get one app plus its environments (subdomains, ports, db engine/name, deploy + health state) and any hostnames of the user's own attached to them. Poll this after awesomate_app_create \u2014 status goes provisioning \u2192 active | failed (read provision_error on failure; to retry, call awesomate_app_create again with the same appSlug, or remove it with awesomate_app_remove_failed).",
42551
42573
  inputSchema: { appId: external_exports.number().int().positive().describe("The app id from awesomate_app_list / _create") }
42552
42574
  },
42553
42575
  async ({ appId }) => {
@@ -42558,6 +42580,24 @@ server.registerTool(
42558
42580
  }
42559
42581
  }
42560
42582
  );
42583
+ server.registerTool(
42584
+ "awesomate_app_remove_failed",
42585
+ {
42586
+ annotations: annotate("awesomate_app_remove_failed", DESTRUCTIVE),
42587
+ description: "Remove an app whose setup FAILED (awesomate_app_get shows status 'failed'), clearing whatever the attempt left on the user's hosting (subdomains, databases, folders). Only failed apps: an active, provisioning or suspended app is refused with 409 not_failed, and removing a working app stays a support request. A failed app already holds no plan slot and does not reserve its name, so to RETRY just call awesomate_app_create again with the same appSlug: the hub clears the failed attempt first. Use this when the user wants the failed app gone, not to retry. Tell the user what will be removed and get a yes first; confirm must equal the app's slug.",
42588
+ inputSchema: {
42589
+ appId: external_exports.number().int().positive().describe("The failed app id from awesomate_app_list / _get"),
42590
+ confirm: external_exports.string().describe("Must equal the app's slug exactly, as proof of intent")
42591
+ }
42592
+ },
42593
+ async ({ appId, confirm }) => {
42594
+ try {
42595
+ return textResult(await hubDelete(requireConfig(), `/api/my-apps/apps/${appId}`, { confirm }));
42596
+ } catch (err) {
42597
+ return errorResult(err);
42598
+ }
42599
+ }
42600
+ );
42561
42601
  server.registerTool(
42562
42602
  "awesomate_app_create",
42563
42603
  {
@@ -44302,7 +44342,7 @@ server.registerTool(
44302
44342
  server.registerTool(
44303
44343
  "awesomate_site_audit",
44304
44344
  {
44305
- description: "Run the SEO + AEO build gates against ONE live page the user owns, fetched AS GPTBot. Returns pass/fail per gate with specific fixes: is the content in the raw HTML at all (a React/Vue site is invisible to ChatGPT, Claude and Perplexity \u2014 only Gemini and Applebot run JavaScript), can the four retrieval bots reach it, is there an accidental noindex/nosnippet, does the canonical point at THIS domain (a leftover pointing at a Replit/Vercel/staging host silently de-indexes the real site), is the structured data valid AND mirrored in visible text, is the content structured so a single section survives being quoted, robots.txt + sitemap, and time-to-first-byte. Run it after building or changing any public page, and before telling the user their site is discoverable \u2014 reads on every plan. Two gates are reported as `skipped` because they need a headless browser or a full crawl; treat skipped as unknown, never as passing.",
44345
+ description: "Run the SEO + AEO build gates against ONE live page the user owns, fetched AS OAI-SearchBot (a retrieval bot, so it sees what ChatGPT search sees; training crawlers like GPTBot are blocked by design on Awesomate-managed domains). Returns pass/fail per gate with specific fixes: is the content in the raw HTML at all (a React/Vue site is invisible to ChatGPT, Claude and Perplexity \u2014 only Gemini and Applebot run JavaScript), can the four retrieval bots reach it, is there an accidental noindex/nosnippet, does the canonical point at THIS domain (a leftover pointing at a Replit/Vercel/staging host silently de-indexes the real site), is the structured data valid AND mirrored in visible text, is the content structured so a single section survives being quoted, robots.txt + sitemap, and time-to-first-byte. Run it after building or changing any public page, and before telling the user their site is discoverable \u2014 reads on every plan. Two gates are reported as `skipped` because they need a headless browser or a full crawl; treat skipped as unknown, never as passing.",
44306
44346
  inputSchema: {
44307
44347
  domain: external_exports.string().min(3).max(253).describe("a domain on the user's own hosting account"),
44308
44348
  path: external_exports.string().max(400).optional().describe("page path to audit, default /")
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@awesomate/hosting-mcp",
3
- "version": "0.88.2",
3
+ "version": "0.88.6",
4
4
  "description": "Awesomate MCP server — lets Claude manage your Awesomate WordPress hosting, plan, limits, n8n automations, and build Node/static apps + databases",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",
@@ -1,5 +1,30 @@
1
1
  {
2
2
  "versions": [
3
+ {
4
+ "version": "0.88.6",
5
+ "highlights": [
6
+ "An app whose setup failed no longer uses one of your app slots or blocks its name: create it again with the same name to retry, or ask Claude to remove it with the new awesomate_app_remove_failed tool"
7
+ ]
8
+ },
9
+ {
10
+ "version": "0.88.5",
11
+ "highlights": [
12
+ "Adding a domain now gives your server's public address, never an internal one, and says the records must be Proxied (orange cloud) in Cloudflare with SSL/TLS set to Full"
13
+ ]
14
+ },
15
+ {
16
+ "version": "0.88.4",
17
+ "highlights": [
18
+ "The site audit now reads your page the way ChatGPT search does, so a page that turns AI bots away is reported as unreachable instead of being scored on the block message, and the sitemap check no longer reports a sitemap missing when it is there",
19
+ "SEO checks test the AI search bots that decide whether you get cited. AI training bots are blocked on purpose on Awesomate-managed addresses, so Claude no longer reports that as a fault"
20
+ ]
21
+ },
22
+ {
23
+ "version": "0.88.3",
24
+ "highlights": [
25
+ "When the hub cannot bring your n8n credential list up to date, Claude now says the list may be out of date, as of when, and which credentials n8n has added or removed since"
26
+ ]
27
+ },
3
28
  {
4
29
  "version": "0.88.2",
5
30
  "highlights": [
@@ -126,7 +126,10 @@ target; never touch prod without an explicit ask.**
126
126
  webhooks/integrations → `node-api-only`; landing page → `static-landing`.
127
127
  It returns **202** with an `appId` + subdomain(s); poll `awesomate_app_get`
128
128
  until `status` is `active` (or `failed` — read `provision_error`, explain
129
- plainly).
129
+ plainly). A failed app holds no plan slot and doesn't keep its name: to
130
+ retry, call `awesomate_app_create` again with the same `appSlug` (the hub
131
+ clears the failed attempt first). If the user would rather drop it, offer
132
+ `awesomate_app_remove_failed` (`confirm` = the slug) after their yes.
130
133
  4. **Scaffold** — call `awesomate_app_scaffold` and write each returned file
131
134
  into a fresh local project folder at its relative path (placeholders are
132
135
  already substituted), then `npm install` (Node) and read the bundled
@@ -257,10 +260,11 @@ keep going on what you can fix.
257
260
  - A `409` with code `app_limit` = the plan's app allowance is full (Support
258
261
  Plus 5, Pro 20 — `awesomate_app_context.limits` has the live numbers).
259
262
  Don't retry; tell the user and surface the `recommendedPlan`/`deepLink`
260
- from the error. **There is no delete-app tool or route** — don't offer
261
- "delete an old one to free a slot", because neither you nor the user can.
262
- The honest options are an upgrade or asking support (awesomate-support
263
- skill) to remove an app.
263
+ from the error. Failed apps never count toward it. There is no tool to
264
+ delete a WORKING app — don't offer "delete an old one to free a slot",
265
+ because neither you nor the user can. The honest options are an upgrade or
266
+ asking support (awesomate-support skill) to remove an app.
267
+ (`awesomate_app_remove_failed` only removes an app whose setup failed.)
264
268
  - Never invent that a capability exists — read `awesomate_app_context` first.
265
269
  - Never build on the hosting account, and never raise a support ticket for a
266
270
  repo or deploy problem yourself: the hub does both the building (via
@@ -242,7 +242,7 @@ MCP, so say that rather than stopping.
242
242
  | `awesomate_domain_add` | Attach a custom domain to a site |
243
243
  | `awesomate_dns_check` | Live answer to "where does this domain actually point" — before and after any DNS change |
244
244
  | `awesomate_site_uptime` | 30-day availability, incidents, downtime per domain (cached, free) |
245
- | `awesomate_site_audit` | SEO + AEO build gates for one page, fetched as GPTBot — content present without JS, retrieval bots reachable, canonical, schema, structure, speed. Not plan-gated. Run it after any public-page change, and see the awesomate-seo skill for the fixes |
245
+ | `awesomate_site_audit` | SEO + AEO build gates for one page, fetched as OAI-SearchBot (what ChatGPT search sees) — content present without JS, retrieval bots reachable, canonical, schema, structure, speed. Not plan-gated. Run it after any public-page change, and see the awesomate-seo skill for the fixes |
246
246
  | `awesomate_files` | Files on their automation account (public/, private/, temp/): put an image or download up and hand back its `public_url`, fetch what a workflow wrote, tidy a folder. Ask before make_public, a move into public/, or delete; `trash` shows what was deleted, and `empty_trash` erases it for good (show what goes, get a clear yes). Support Plus+, owner's file-folders switch on (`status` says what's missing). Guide: https://hub.awesomate.ai/docs/mcp/guides/files/ |
247
247
  | `awesomate_snapshot_site` | Before ANY change to a live site — files + DB restore point |
248
248
  | `awesomate_list_snapshots` | What restore points a site has |
@@ -159,7 +159,7 @@ the robots rules, the JSON-LD block, and tell them precisely where each goes
159
159
  ## 3. Verify — run the gates, don't eyeball it
160
160
 
161
161
  **`awesomate_site_audit { domain, path }`** runs the mechanical gates against
162
- the live page, fetched *as GPTBot*, and returns pass/fail with specific fixes:
162
+ the live page, fetched *as OAI-SearchBot* (what ChatGPT search sees), and returns pass/fail with specific fixes:
163
163
  content present without JavaScript, all four retrieval bots reachable, no
164
164
  accidental `noindex`/`nosnippet`, canonical pointing at this domain, valid
165
165
  structured data whose facts also appear in the copy, section structure,
@@ -181,10 +181,14 @@ Then confirm by hand what the tool can't judge:
181
181
  - **Content is in the raw HTML** — `curl -s https://DOMAIN/page` shows the
182
182
  actual content, not an empty `<div id="root">`. AI crawlers don't run
183
183
  JavaScript; a client-rendered page is invisible to them.
184
- - **AI crawlers aren't blocked at the edge** — a `curl` with a GPTBot /
185
- ClaudeBot user-agent returns 200, not a 403/Cloudflare challenge (Cloudflare
186
- blocks many AI bots by default; robots.txt can't fix that). Commands + where
187
- to fix each case: [references/ai-discoverability.md](references/ai-discoverability.md).
184
+ - **AI retrieval bots aren't blocked at the edge** — a `curl` as OAI-SearchBot,
185
+ Claude-SearchBot, PerplexityBot, Perplexity-User, ChatGPT-User and Bingbot
186
+ returns 200, not a 403/Cloudflare challenge (robots.txt can't fix an edge
187
+ block). **Training bots (GPTBot, ClaudeBot, CCBot…) are not the test:** on
188
+ Awesomate-managed domains (`*.awesomate.site`, `*.awesomate.app`) they get a
189
+ 403 by design, and on the user's own domain letting them in is the user's
190
+ choice. Commands + where to fix each case:
191
+ [references/ai-discoverability.md](references/ai-discoverability.md).
188
192
  Tell the user the next step is submitting the sitemap in Google Search Console
189
193
  (you can't do that for them) — offer a one-paragraph how-to if they want it.
190
194
 
@@ -203,9 +207,12 @@ the user asks to "check SEO", and after any content change:
203
207
  the page standing in for it — check whichever they have), and the content
204
208
  still mentions the current offerings. A stale llms.txt misdescribes the
205
209
  business to every AI assistant that reads it.
206
- 4. **AI crawlers not blocked at the edge** —
207
- `curl -A "GPTBot" -s -o /dev/null -w "%{http_code}" https://DOMAIN/`
208
- (repeat with `ClaudeBot`) returns 200, not 403 or a challenge page.
210
+ 4. **AI retrieval bots not blocked at the edge** —
211
+ `curl -A "Mozilla/5.0 (compatible; OAI-SearchBot/1.0; +https://openai.com/searchbot)" -s -o /dev/null -w "%{http_code}" https://DOMAIN/`
212
+ (repeat as Claude-SearchBot, PerplexityBot, Perplexity-User, ChatGPT-User
213
+ and Bingbot) returns 200, not 403 or a challenge page. Don't test with
214
+ GPTBot or ClaudeBot: those are training bots, blocked by design on
215
+ Awesomate-managed domains and the owner's choice elsewhere.
209
216
  Where to fix each case: [references/ai-discoverability.md](references/ai-discoverability.md).
210
217
  5. **WordPress only: `blog_public` must be 1** — one checkbox ("Discourage
211
218
  search engines") silently de-indexes the whole site. Read it via
@@ -58,39 +58,53 @@ Always check the live `https://DOMAIN/robots.txt`, not just the file you wrote.
58
58
 
59
59
  ## robots.txt allows it — but the CDN can still 403 it
60
60
 
61
- Cloudflare **blocks many AI crawlers by default on new zones** (GPTBot,
62
- ClaudeBot, PerplexityBot, CCBot, Google-Extended). The crawler gets a 403 or a
63
- challenge page before it ever reads robots.txt, so a perfect robots.txt proves
64
- nothing. Always verify from the outside:
61
+ Cloudflare can block AI crawlers at the edge, and steers new zones into doing
62
+ so. The crawler gets a 403 or a challenge page before it ever reads robots.txt,
63
+ so a perfect robots.txt proves nothing. Always verify from the outside, with the
64
+ **retrieval** bots — the ones whose access decides whether the site is cited:
65
65
 
66
66
  ```bash
67
- curl -s -o /dev/null -w "%{http_code}\n" -A "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; GPTBot/1.0; +https://openai.com/gptbot)" https://DOMAIN/
68
- curl -s -o /dev/null -w "%{http_code}\n" -A "Mozilla/5.0 (compatible; ClaudeBot/1.0)" https://DOMAIN/
69
- curl -s -o /dev/null -w "%{http_code}\n" -A "Mozilla/5.0 (compatible; PerplexityBot/1.0)" https://DOMAIN/
67
+ for ua in \
68
+ "Mozilla/5.0 (compatible; OAI-SearchBot/1.0; +https://openai.com/searchbot)" \
69
+ "Mozilla/5.0 (compatible; Claude-SearchBot/1.0; +https://anthropic.com)" \
70
+ "Mozilla/5.0 (compatible; PerplexityBot/1.0; +https://perplexity.ai/perplexitybot)" \
71
+ "Mozilla/5.0 (compatible; Perplexity-User/1.0; +https://perplexity.ai/perplexity-user)" \
72
+ "Mozilla/5.0 (compatible; ChatGPT-User/1.0; +https://openai.com/bot)" \
73
+ "Mozilla/5.0 (compatible; bingbot/2.0; +http://www.bing.com/bingbot.htm)"; do
74
+ printf '%s %s\n' "$(curl -s -o /dev/null -w '%{http_code}' -A "$ua" https://DOMAIN/)" "$ua"
75
+ done
70
76
  ```
71
77
 
72
78
  200 = reachable. 403, 503, or an HTML page mentioning Cloudflare = blocked at
73
- the edge. Where to fix it:
74
-
75
- - **Awesomate-managed domain** (`*.awesomate.site` etc.): the zone is managed
76
- by Awesomate — the user can't change it. Raise it via `awesomate_support`
77
- so the zone's AI Crawl Control settings get reviewed.
78
- - **Custom domain on the user's own Cloudflare account**: fix it with a WAF
79
- custom rule, action **Skip**, expression:
80
-
81
- ```
82
- cf.verified_bot_category in {"AI Crawler" "Search Engine Crawler"}
83
- ```
84
-
85
- Match on the **verified category, never on user-agent strings** — a UA
86
- allowlist is trivially spoofed and silently rots as bots are renamed.
87
- Cloudflare's own AI-bot toggle does not separate citation bots from training
88
- bots, which is why the toggle alone is the wrong instrument.
79
+ the edge.
80
+
81
+ **Training bots (GPTBot, ClaudeBot, CCBot, Bytespider, Amazonbot…) are a
82
+ different question.** On Awesomate-managed domains (`*.awesomate.site`,
83
+ `*.awesomate.app`) they get `403 Your request was blocked.` **by design** — that
84
+ is policy, not a fault, and it does not affect citation. Don't report it and
85
+ don't ask for it to be lifted. On the user's own domain, letting training bots
86
+ in is the user's choice.
87
+
88
+ Where to fix a blocked **retrieval** bot:
89
+
90
+ - **Awesomate-managed domain**: the zone is managed by Awesomate — the user
91
+ can't change it. Raise it via `awesomate_support`.
92
+ - **Custom domain on the user's own Cloudflare account**: in **AI Crawl
93
+ Control**, set each blocked retrieval bot (OAI-SearchBot, Claude-SearchBot,
94
+ PerplexityBot, Perplexity-User, ChatGPT-User) to **Allow**; training bots are
95
+ their call. If Cloudflare's **managed robots.txt** is on, check the live
96
+ `/robots.txt` doesn't disallow the retrieval bots either. Don't write a WAF
97
+ skip on `cf.verified_bot_category "AI Crawler"`: that category is the
98
+ training crawlers, and PerplexityBot isn't a verified bot (since Aug 2025),
99
+ so a category rule can't match it anyway.
89
100
 
90
101
  Verified 2026-09-02 across awesomate.ai, hub.awesomate.ai and eight live client
91
- sites: all four retrieval bots got **200**, so this is not currently biting the
92
- fleet. Re-check per site rather than assuming either way — it is a per-zone
93
- setting and new zones are steered into blocking at signup.
102
+ sites: all four retrieval bots got **200**. **That is now stale:** on
103
+ 08/10/2026 PerplexityBot was found getting 403 on Awesomate-managed zones
104
+ (OAI-SearchBot, Claude-SearchBot, Perplexity-User, ChatGPT-User and Bingbot were
105
+ fine). It is a known Cloudflare fault being fixed — PerplexityBot should get
106
+ 200; if a managed site still shows 403 for it, raise it with support rather
107
+ than treating it as expected. Re-check per site rather than assuming either way.
94
108
 
95
109
  ## Most AI crawlers do not run JavaScript
96
110