BitNinja WebShield Blocks Royal MCP OAuth Discovery
If Claude, ChatGPT, or Cursor fails to connect to your Royal MCP endpoint and a direct curl against /.well-known/oauth-authorization-server returns an HTML page titled “One moment, please...” with an auto-reload script, your host is running BitNinja WebShield in front of WordPress. WebShield serves a JavaScript CAPTCHA challenge for these paths — MCP clients don’t execute JavaScript, so the OAuth handshake dies at discovery. Fix is at the hosting layer; no WordPress setting will resolve it.
Royal MCP connection issues that look host-specific are often a plugin conflict, cache layer, or stale OAuth state — work through our Troubleshooting Start Here checklist first if you haven’t already. This article assumes the basics are ruled out and the curl fingerprint below matches.
Symptoms
You’re hitting this specific issue if the following are true:
The Telltale Signs
- Claude.ai, ChatGPT, or Cursor fails to connect to your Royal MCP endpoint — often silently or with a reference code the client can’t explain
- A
curlagainst/.well-known/oauth-authorization-serverreturns HTTP 200 with aContent-Type: text/htmlbody containing<title>One moment, please...</title>and an auto-reloadsetTimeout(function(){window.location.reload();}, 5000) - The HTML body includes obfuscated JavaScript that references
wsidchk(a form parameter) andwebdriverCheck(a bot-fingerprint function name) - Reproduces on ALL User-Agents — browser UAs, plain
curl,python-httpx— the challenge fires unconditionally on the path - Royal MCP’s Activity Log (WP Admin → Royal MCP → Activity Log) stays empty for every failed connection attempt — the requests never reach WordPress
- Your hosting provider is a shared cPanel host running CloudLinux — often layered on top of Imunify360
BitNinja WebShield’s JavaScript CAPTCHA relies on a real browser executing the challenge script, setting a clearance cookie, and reloading the page. MCP clients (Claude, ChatGPT, Cursor) are headless HTTP clients — they don’t run a JavaScript engine, don’t maintain a browser cookie jar for challenge responses, and don’t reload. The response body they receive is HTML where they expected JSON, so the OAuth handshake aborts at discovery.
How to Confirm It’s BitNinja WebShield
A 30-second curl test confirms the signature. Run from any terminal — replace example.com with your actual domain:
Test your OAuth discovery URL
curl -i https://example.com/.well-known/oauth-authorization-server
The diagnostic fingerprint is HTTP 200 with Content-Type: text/html and a response body starting with:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf8">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<script>
(function(){
setTimeout(function(){
window.location.reload();
}, 5000);
}())
</script>
<link rel="icon" href="data:,">
<title>One moment, please...</title>Grep the response body for BitNinja’s signature tokens
curl -s https://example.com/.well-known/oauth-authorization-server | grep -oE "wsidchk|webdriverCheck"
If both wsidchk and webdriverCheck appear in the output, you’re looking at BitNinja WebShield’s JS challenge. These tokens only co-occur in WebShield’s obfuscated fingerprinting script.
Confirm the block is path-based, not domain-wide
curl -I https://example.com/
Your site homepage should return HTTP 200 with normal WordPress output (no interstitial). This proves the intercept is scoped to specific paths — the rest of the site works normally.
BitNinja WebShield sits in front of WordPress at the web-server layer, so denied requests never reach PHP and never register in the plugin’s Activity Log. An empty log combined with a Claude connection failure isn’t proof of WebShield by itself — but combined with the curl fingerprint above, it’s conclusive.
The Fix — Ask Your Host to Disable WebShield for the OAuth Paths
BitNinja WebShield runs at the hosting provider’s layer, not inside WordPress. The fix is a support ticket to your host asking them to exclude specific paths from WebShield (or disable WebShield for the whole domain). Most hosts complete this in under 24 hours — it’s a routine change on their end.
Ready-to-paste hosting-provider request
Copy the text below into a support ticket with your hosting provider. Substitute example.com with your actual domain:
Subject: Please exclude OAuth paths from BitNinja WebShield for example.com Hi, We’re running the Royal MCP WordPress plugin on example.com, which lets AI assistants like Claude, ChatGPT, and Cursor connect to our site via the Model Context Protocol (MCP). The connection requires OAuth 2.0 discovery and token endpoints to be reachable without a JavaScript challenge. Right now BitNinja WebShield is intercepting these paths and returning an HTML CAPTCHA page (title: “One moment, please...”) with the wsidchk / webdriverCheck challenge script. MCP clients don’t execute JavaScript, so the OAuth handshake dies at discovery. Please exclude the following URL paths for example.com from BitNinja WebShield (or, equivalently, disable WebShield for the whole domain): /wp-json/* /.well-known/* /authorize /token /register Important: please use a path-based or domain-level exclusion, NOT an IP allowlist. Claude.ai and other MCP client backends connect from constantly-rotating IP ranges, so an IP-based rule will silently break within a few weeks. Path or domain scope is the correct mechanism. These are standard WordPress REST API + OAuth 2.0 (RFC 8414) endpoints. Excluding them from WebShield will not reduce security — they are the plugin’s own authenticated endpoints subject to WordPress’s standard capability checks. Thanks in advance.
Claude.ai and Anthropic’s backend connect from constantly-rotating IP ranges. You can’t whitelist a fixed set of Anthropic IPs, so path-based exclusion is the correct approach. The paste-ready text above already explains this — point your host at this article if they need further convincing.
Retry the connection
Once your host confirms the exclusion is in place, the fix is immediate:
- Re-run the curl test from the “How to Confirm” section above. You should now see a normal OAuth metadata JSON response, not the HTML challenge page.
- In your MCP client, delete the existing Royal MCP connector if present, wait 30 seconds, then re-add it fresh. This forces the full OAuth flow from scratch rather than reusing cached failure state.
- Complete the authorize consent screen. Royal MCP’s Activity Log should now show entries the moment you click “Authorize.”
What NOT to Try (WordPress-Side Fixes Won’t Help)
BitNinja WebShield runs at the web-server layer, ahead of PHP. That means the following common troubleshooting steps will NOT resolve this issue — don’t waste time on them:
- Changing Royal MCP settings. No plugin setting can affect requests that never reach PHP. Same for other security or cache plugins.
- Deactivating other plugins. If WebShield is the block, no combination of active/inactive WordPress plugins will change the outcome.
- Adding
.htaccessrewrite rules. Apache/LiteSpeed process.htaccessAFTER WebShield has already served the challenge. Rules won’t fire. - Switching to a default theme. Themes only render pages that WordPress successfully processes — irrelevant here.
- Whitelisting IPs in WordPress security plugins. Those plugins don’t see the request either. Only WebShield’s exclusion matters.
- Asking your host for an IP allowlist. Anthropic’s outbound IPs rotate; an IP-based rule silently breaks within weeks. Insist on a path-based or domain-level exclusion.
- Uninstalling & reinstalling Royal MCP. The plugin is fine — the problem is upstream of WordPress.
What Royal MCP 1.4.39 Already Does for You
Royal MCP 1.4.39 detects this exact failure mode automatically. When the plugin’s well-known-endpoint self-check sees an HTML response containing both wsidchk and webdriverCheck, it surfaces a dismissible admin notice on the Plugins screen and Royal MCP settings linking back to this page.
So future customers on hosts running BitNinja WebShield will land here from inside their own WordPress admin instead of having to search for “Royal MCP can’t connect” and stumble across the diagnosis. The plugin can’t auto-fix the block for you — WebShield is upstream of WordPress and the customer’s hosting-provider exclusion is the only mechanism — but the notice makes the diagnosis fast.
Royal MCP’s well-known probe runs from the site to itself (a WordPress-loopback HTTP request). Some WebShield configurations exclude loopback traffic and some don’t. If loopback is excluded but external traffic still gets the challenge, our self-check will see a valid metadata response and NOT trigger the notice — even though Claude (external) still can’t connect. If you see this pattern, run the curl tests in the “How to Confirm” section above from EXTERNAL to your site — those are the definitive diagnostic.
Still Stuck? Two Support Paths
If your host completed the allowlist and Claude still can’t connect:
Community Support (free) — wp.org Plugin Forum
Post a new thread at wordpress.org/support/plugin/royal-mcp/. Include the diagnostic info listed below in your post.
Premium Support (paid) — direct one-on-one help
For priority response (24-hour SLA) and hands-on diagnostic help, our Premium Support tier is $149/year.
Information to include in your post or ticket
- Full curl output for
/wp-json/royal-mcp/v1/mcpand/.well-known/oauth-authorization-serverBEFORE and AFTER your host applied the allowlist (usecurl -i, not just-I, so we see the body) - Your hosting provider (SiteGround, Bluehost, HostGator, Namecheap, etc.) — helps us cross-reference known-good allowlist patterns for that host
- Confirmation your host completed the allowlist (paste any confirmation email they sent)
- Royal MCP version from WP Admin → Plugins
- Whether the Activity Log now shows entries during a failed attempt (any entry means the request reached WordPress — different diagnosis)