Support / Royal MCP / OAuth Discovery Returns HTML Instead of JSON

OAuth Discovery Returns HTML Instead of JSON

Royal MCP’s admin self-check flagged that /.well-known/oauth-authorization-server is returning an HTML document at HTTP 200 instead of the JSON metadata MCP clients expect. A membership plugin, theme template, or restricted-content rule is intercepting the request and serving its own login or access-denied page. Claude.ai and other discovery-aware MCP clients fail silently when they receive HTML on this URL. This page walks through identifying the culprit and three fixes that don’t require uninstalling anything.

⚠️ Wrong page if your response is JSON (not HTML)

This doc covers the case where /.well-known/oauth-authorization-server returns HTML (a login page, access-denied screen, or theme template). If your discovery endpoint returns JSON but the issuer field points to an auth.<subdomain> instead of your site root — or the scopes_supported includes openid / profile / email — that’s a different pattern (Cloudflare Zero Trust hijacking discovery) with a different fix.

Cloudflare Zero Trust is hijacking MCP discovery →

Detected by Royal MCP 1.4.22

This admin notice was added in Royal MCP 1.4.22. The self-check probes your discovery URL every 12 hours and classifies the response. If you arrived here from the admin notice link, the plugin has already confirmed your response is HTML (not JSON) — jump straight to the Three Fixes section.

Confirm What’s Happening

From any terminal on your local machine, run:

Mac Terminal, Linux, Windows Command Prompt, Git Bash:

curl -i https://example.com/.well-known/oauth-authorization-server

Windows PowerShell: (PowerShell aliases curl to Invoke-WebRequest with different syntax — use curl.exe to bypass the alias)

curl.exe -i https://example.com/.well-known/oauth-authorization-server

You’re hitting this issue (HTML hijack) if the response is:

The Telltale Signature

  • Status: HTTP/2 200 (not 404, not 301)
  • Body: starts with <!DOCTYPE html>, <html>, or <head> — an HTML document, not JSON
  • Body content: typically a login page, access-denied notice, password prompt, or your site’s 404 template
  • Headers: may include cookies like armember_*, mepr_*, rcp_*, wp-postpass_*, or a session cookie set by your theme
If the response is JSON instead of HTML

You’re looking at a different issue. Check the issuer field in the JSON:

  • If issuer matches your site root (https://your-site.com) and scopes_supported is ["mcp:full"] — discovery is healthy. The connection failure is elsewhere. See the troubleshooting start-here page.
  • If issuer points to auth.<sub> or scopes_supported includes OIDC scopes (openid, profile, email) — Cloudflare Zero Trust Access is intercepting your discovery endpoint. See Cloudflare Zero Trust hijacks MCP discovery for the fix.

Why This Happens

Royal MCP registers a top-level rewrite rule mapping /.well-known/oauth-authorization-server to its OAuth metadata handler. The rule resolves at the WordPress rewrite layer, and the handler returns JSON. That’s the working path.

But several classes of plugins and themes hook earlier than the OAuth handler and short-circuit the response before it runs:

In all cases the symptom is the same: discovery clients ask for JSON, get HTML, and fail silently without a clear error message.

Three Fixes

Try them in order. Fix #1 is the cheapest. Fix #3 narrows down the culprit if the first two don’t resolve it.

Fix #1 — Add OAuth paths to your membership plugin’s unrestricted-URL list

Almost every membership plugin has an “Excluded URLs” or “Bypass Pages” setting. The five Royal MCP paths to allow are:

/.well-known/oauth-authorization-server
/.well-known/oauth-protected-resource
/authorize
/token
/register

Add each as an exact match or a wildcard pattern depending on what the plugin supports. Save the setting and re-test with the curl command above.

Plugin-specific paths to the setting:

  • ARMember → ARMember → Plugin Configuration → Login tab → “Restrict Pages” or “Page Restriction” section
  • MemberPress → MemberPress → Settings → Pages tab → “Unauthorized Access” section
  • Restrict Content Pro → Restrict → Settings → Misc tab → “Exempted URLs” or “Excluded URIs”
  • Paid Memberships Pro → Memberships → Settings → Advanced tab
  • BuddyBoss → BuddyBoss → Settings → Pages tab → Login redirect settings

Fix #2 — Re-save Permalinks to flush rewrite rules

WordPress rewrite rules are cached, and the order they evaluate in can be reshuffled by plugin activations and deactivations. Re-saving forces a fresh build and gives Royal MCP’s rules a clean priority.

Go to WP Admin → Settings → Permalinks and click Save Changes without changing anything else. Then re-test with the curl command above.

If you’re using a custom permalink structure

Royal MCP’s rewrite rules work on every standard permalink structure (Post name, Day and name, Month and name, Numeric, Custom Structure starting with /%postname%/). The Plain permalink structure (no rewriting at all) is the only configuration where these rules can’t be registered — switch to Post name if you’re currently on Plain.

Fix #3 — Bisect the culprit by deactivating suspects

If Fixes #1 and #2 don’t resolve it, identify which plugin is intercepting by deactivating candidates one at a time and re-running the curl test after each. Start with:

  1. Membership / access-control plugins
  2. Coming-soon / maintenance-mode plugins (these are often forgotten and left active in “authenticated bypass” mode that doesn’t bypass for non-browser requests)
  3. Security plugins with custom WAF rules
  4. Any other plugin whose name contains “Restrict”, “Lock”, “Private”, “Members”, “Force Login”, etc.
  5. Your active theme — switch temporarily to Twenty Twenty-Five and re-test; if the issue disappears, your theme has a template-level page lock

Once identified, you have three options:

  • Configure the plugin/theme to allow the OAuth paths (the equivalent of Fix #1 for non-membership plugins)
  • If the plugin offers a per-URL filter hook, write a small must-use plugin that bypasses for OAuth URLs only
  • Switch to a different plugin that handles allowlists more cleanly

Verify the Fix Worked

After applying any of the three fixes:

  1. Run the curl command from the top of this page again — the response should now be JSON starting with {"issuer":"https://..."}
  2. In WordPress admin, go to WP Admin → Royal MCP → Settings and save the form (this invalidates the 12-hour self-check transient so the notice refreshes immediately)
  3. Reload any Royal MCP admin page — the “OAuth discovery is being served as HTML” notice should no longer appear
  4. Reconnect Claude.ai (or whichever MCP client was failing) — the OAuth handshake should now complete

Still Stuck? Two Support Paths

If you’ve worked through all three fixes and the curl test still returns HTML:

Community Support (free) — wp.org Plugin Forum

Post a new thread at wordpress.org/support/plugin/royal-mcp/. The Royal Plugins team monitors the forum regularly and community members often help disambiguate plugin conflicts faster than email could.

Premium Support (paid)

For direct one-on-one help with 24-hour SLA, see Premium Support ($149/year).

Information to include (either path)

  • The full curl -i output for /.well-known/oauth-authorization-server (headers AND body, redacted if your error page contains sensitive content)
  • Your active theme name
  • Your list of active plugins (WP Admin → Plugins → copy/paste the list)
  • Royal MCP version (must be 1.4.22 or newer for the self-check to apply)
  • Whether the issue persists after switching to Twenty Twenty-Five and disabling all non-essential plugins