rpteam
Forum Replies Created
-
Thanks for the detailed write-up — that level of triage actually narrows this down a lot.
Your symptom matches a pattern we now see frequently on managed shared hosts (SiteGround included): the OAuth consent screen renders fine because your browser hits /authorize, but the
server-to-server POST /token step is made by Claude’s broker with User-Agent: python-httpx/0.28.1 from Anthropic’s IP range 160.79.104.0/22. On several managed stacks, the host’s edge security layer (separate from any page/cache exclusion in the optimizer plugin) blocks that User-Agent or that IP range before the request reaches WordPress. The request never hits our PHP, so it looks like our /token rejected it — but really it never arrived.A few minutes of work should tell us if this is your case.
- The one log line that routes this in a single round-trip Go to WP Admin → Royal MCP → Logs (or Activity Log on slightly older versions). Reproduce the failure, then look for entries around that timestamp. Three possible shapes:
- A row for /token with error + description (e.g. invalid_grant: …, invalid_client: …, invalid_request: …) — that’s a PHP-layer rejection, fixable cleanly.
- A row for /authorize but no row for /token at all — that’s the host-WAF-blocking-the-token-POST pattern. Anthropic’s request never reached us.
- No rows at all for the attempt — same family as the above; the edge layer is dropping requests before they hit WordPress.
Paste whichever you see
- Two curl probes you can run yourself to confirm (or rule out) the edge block From any external machine (your laptop is fine):
curl -A “Mozilla/5.0 Chrome/120.0” \
-X POST https://dizionedigitale.it/register \
-H “Content-Type: application/json” \
-d ‘{“redirect_uris”:[“http://localhost”%5D,”client_name”:”diag”}’
curl -A “python-httpx/0.28.1” \
-X POST https://dizionedigitale.it/register \
-H “Content-Type: application/json” \
-d ‘{“redirect_uris”:[“http://localhost”%5D,”client_name”:”diag”}’
If the browser-UA probe returns 201 Created (or a JSON invalid_request 400) but the python-httpx-UA probe returns a 403/406/429 — confirmed. The fix is on the host side: ask SiteGround support to allow User-Agent: python-httpx/* on these paths, or to allow Anthropic’s IP range 160.79.104.0/22, or to add path exclusions for /register, /authorize, /token, /wp-json/royal-mcp/*, /.well-known/oauth-authorization-server, /.well-known/oauth-protected-resource. SiteGround support can usually action this same-day. - Two other possibilities worth quickly eliminating while you wait on the log entry
- Static client secret saved? In Royal MCP → Settings → Advanced, the Generate button populates the input but doesn’t persist until you click Save Settings at the bottom of the page.
If Client ID was saved but Client Secret was Generated-then-not-Saved, the token POST fails with invalid_client. Reload that admin page — both fields should still show values after
the reload. If either is blank after reload, click Generate + Save Settings, then re-enter both into the Claude connector’s Advanced Settings (fresh paste — don’t reuse the prior
values). - Static .well-known/ files — you said you verified single https:// prefixes, but please double-check all four fields in both files (issuer, authorization_endpoint, token_endpoint,
registration_endpoint). A common gotcha is the first field looking right while one of the endpoint lines silently has https://https://.
Just curl https://dizionedigitale.it/.well-known/oauth-authorization-server and eyeball the JSON.
If you need MCP working immediately while we sort this Claude Desktop’s API-key path bypasses OAuth entirely — no /register, /authorize, /token involved, so the edge-WAF question doesn’t apply. Setup is one block in your Claude Desktop config using mcp-remote –header X-Royal-MCP-API-Key: . Walkthrough: https://royalplugins.com/support/royal-mcp/connect-claude-desktop-api-key.html. Customers blocked on Claude.ai web for host-WAF reasons often use this as the working transport while their host adjusts the rules. Send the log entry (or the curl results) and we’ll close this out.
ChatGPT Guide
We just published a step-by-step walkthrough for connecting Royal MCP to
ChatGPT:https://royalplugins.com/support/royal-mcp/connecting-to-chatgpt/
What’s in it:
- Enabling Developer Mode in ChatGPT (where “Custom Connectors” recently got
renamed to “Apps”) - Adding Royal MCP as a Custom App with OAuth
- 5 step-by-step screenshots + a 90-second OAuth walkthrough video
- Troubleshooting table covering the known “unknown error” UX quirk that appears
at the end of the OAuth flow but doesn’t actually block the connection - Transparent framing on ChatGPT’s plan tier gating: Plus and Pro tiers can read your
WordPress data through Royal MCP but can’t write to it; full read + write
requires Business, Enterprise, or Edu. Worth knowing before you set it up if
your use case is content management. (Claude.ai has no such tier gate, so it’s
the easier path for “AI publishes my blog post” workflows on personal accounts.)
If you hit any issue that’s not covered in the troubleshooting table, please
open a new thread and include: which ChatGPT plan you’re on, which step failed,
and a screenshot of the Royal MCP Activity Log with the most recent oauth:* rows
visible.Glad to report we got this connected and working. Sharing the two issues we
identified — both fixed now — in case anyone else lands here with similar
symptoms.Issue 1: ambiguous placeholder in our SiteGround static-files walkthrough
Our /.well-known/ 404 fix walkthrough
(https://royalplugins.com/support/royal-mcp/siteground-well-known-404.html) used
YOUR-DOMAIN.com as a placeholder inside the JSON template. The intent was “swap
with your bare domain,” but a reasonable reading was to swap with the full URL
https://yoursite.com — which produced https://https://yoursite.com/authorize
(etc.) in the saved static file. Browsers then parse the resulting URL with
https as the hostname, DNS lookup fails, and you get a
DNS_PROBE_FINISHED_NXDOMAIN / “site can’t be reached” page when OAuth tries to
redirect.If you see doubled https://https:// on any of the OAuth endpoint URLs in your
/.well-known/oauth-authorization-server response, edit your static files and
remove the duplicates. We’ve patched the walkthrough to use a literal
example.com placeholder with explicit “do NOT include https:// in your
replacement” guidance and a verification step, and swept the same fix across the
rest of our support docs so this shouldn’t trip anyone else.Issue 2: Microsoft Store version of Claude Desktop uses a sandboxed config path
If you installed Claude Desktop from the Microsoft Store, it’s sandboxed and
reads/writes config from
%LOCALAPPDATA%\Packages\Claude_pzs8sxrjxfjjc\LocalCache\Roaming\Claude\ — NOT
the standard %APPDATA%\Claude\ that every Anthropic doc and our own walkthroughs
reference. Manual edits to the config at the standard path get silently ignored, restart Claude Desktop all you want, nothing picks up.Two fixes:
- Uninstall the Microsoft Store version and reinstall from https://claude.ai/download (recommended — every documented walkthrough then
works as written), or… - Put your config at the sandboxed path above We’ve added a warning to the Connecting to Claude walkthrough (https://royalplugins.com/support/royal-mcp/connecting-to-claude/#browser-and-desktop) so this is surfaced upfront on Windows installs. Marking this resolved. If anyone hits either of these and the guidance above doesn’t get you unstuck, please open a fresh thread.
RP Team
- This reply was modified 2 weeks, 1 day ago by rpteam. Reason: typo
Hi there,
You’ve read the changelog correctly — user_email and user_login were
intentionally removed from wp_get_users / wp_get_user in 1.2.3 to reduce the PII
surface accessible via MCP, and there’s no filter to opt them back in today.
For a one-time CRM mapping job, two equivalent paths — whichever fits your
hosting setup:
Via wp-cli (if you have SSH access on your server — most managed hosts do):
wp user list –fields=ID,user_email,roles –format=csv > users.csv
Clean CSV with the three fields you need, ready to import into your CRM.
Via SQL (phpMyAdmin, Adminer, or any DB client):SELECT u.ID, u.user_email, um.meta_value AS roles_serialized
FROM wp_users u
LEFT JOIN wp_usermeta um ON um.user_id = u.ID AND um.meta_key =
‘wp_capabilities’;Adjust the wp_ table prefix to match your install. The roles_serialized column
comes back as PHP-serialized strings like a:1:{s:10:”subscriber”;b:1;} — most
spreadsheet apps can extract the role name with a quick regex or Text to Columns
split on “, or you can unserialize() in PHP if you’re processing the export
programmatically.For the broader question of exposing user_email through MCP for ongoing or
automated use cases, we’re considering opt-in patterns (admin-only scope,
explicit setting toggle) for a future release. No timeline yet — the design
needs to balance legitimate use cases like yours against broader data-exposure
considerations, and that warrants more thought than a quick filter add.Thanks for raising it with security in mind already — makes the conversation
easier on our end.RP Team
Hi — quick correction to my last reply. After running deeper probes against your site, I’ve identified the actual root cause and it’s NOT one of the five invalid_grant / invalid_request cases I asked you to
check for. It’s a layer above.The block is at your hosting provider’s edge, not at Royal MCP.
Reproducible from any external machine:
$ curl -A “python-httpx/0.27.0” https://kemoso.com/.well-known/oauth-authorization-server
HTTP/1.1 429 Too Many Requests
Content-Type: text/html; charset=iso-8859-1
Too Many Requestsvs.
$ curl -A “Mozilla/5.0” https://kemoso.com/.well-known/oauth-authorization-server
HTTP/1.1 200 OK
Content-Type: application/json
{“issuer”:”https://kemoso.com”,”authorization_endpoint”:”https://kemoso.com/authorize”, … }Confirmed across two separate probes three minutes apart — the 429 is persistent and User-Agent-targeted, not burst rate limiting. The plain text/html; charset=iso-8859-1 body shape is a cPanel-managed-security
signature, likely Imunify360 or mod_security with a UA filter rule active on your account.Why this matches your symptom perfectly:
- You open /authorize in your browser → 200 OK (real Chrome UA, not blocked)
- You click Authorize → consent screen renders correctly ✅
- Behind the scenes, Anthropic’s backend POSTs /token using User-Agent: python-httpx/* → Xneelo’s WAF returns 429 before the request ever reaches your WordPress install
- Your client sees mcp_token_exchange_failed
- The oauth:token → ERROR row you see in the Activity Log is most likely from the browser-side /authorize step, OR a single intermittent /token POST that slipped through. The dominant failure mode is the 429 at the edge.
Fix — Xneelo support ticket
Royal MCP can’t change what User-Agent Anthropic sends. The fix has to happen at the Xneelo / WAF layer. Paste this verbatim into a support ticket with Xneelo:
Hi Xneelo support,
My WordPress site at kemoso.com runs a plugin (Royal MCP) that needs to accept OAuth requests from a service that uses the User-Agent string python-httpx/0.27.0. Your managed security stack is currently returning 429 Too Many Requests for any request with that User-Agent — even for single, non-bursty requests.
Reproducible from any external IP:
curl -A "python-httpx/0.27.0" https://kemoso.com/.well-known/oauth-authorization-server
returns 429 Too Many Requests, while
curl -A "Mozilla/5.0" https://kemoso.com/.well-known/oauth-authorization-server
returns 200 OK with the expected JSON response.
Could you please either:
1. Whitelist User-Agent: python-httpx/* on this domain, OR
2. Exclude these specific paths from User-Agent-based filtering: /register, /authorize, /token, /.well-known/oauth-authorization-server, /.well-known/oauth-protected-resource
The whitelist is preferable as it allows the plugin's authentication to work site-wide. The path exclusion is an acceptable fallback.
Thanks!Once Xneelo applies that change, the OAuth flow should complete end-to-end. No changes needed on the WordPress / Royal MCP side.
RP Team
Hi,
Good diagnostic info — the fact that the Activity Log shows oauth:token → ERROR (rather than being empty) is the key signal. It means Anthropic’s backend IS reaching your WordPress install and the /token POST IS landing at our handler. So this isn’t a WAF or trailing-slash issue. The /token handler itself is rejecting the request — and the exact error_description it logged tells us which of 5 known failure modes you’re hitting.
I ran external probes against kemoso.com just now to confirm the plugin layer is healthy:
- GET /.well-known/oauth-authorization-server → 200
- GET /token → 405 (correct — POST required)
- POST /token with empty body → 400 with the proper OAuth JSON error response
So Royal MCP is configured correctly and responding correctly. The next step is reading the actual error your handler emitted.
What I need from you: in WP Admin → Royal MCP → Activity Log, click View Details on the most recent oauth:token ERROR row and screenshot the panel (or paste the JSON). The error_description field is what we need. It will be one of:
- invalid_grant: Authorization code is invalid, expired, or already used. — typically host object cache evicting the auth code between /authorize and /token. The DB-table fix in 1.4.17 should prevent this on 1.4.22, but if you’re seeing it I want to know.
- invalid_grant: PKCE verification failed. — usually a security plugin URL-decoding the POST body before it reaches the OAuth handler. Important: WP Ghost being deactivated isn’t
always enough — some of its filters and rewrites persist after deactivation. If your error_description is this one, try fully uninstalling WP Ghost (not just deactivating) and
re-test. - invalid_grant: redirect_uri mismatch. — Claude’s stored redirect_uri doesn’t match what /authorize received. Fix: Royal MCP → Settings → Reset OAuth State, then delete the
connector in Claude.ai (wait 30s) and re-add it from scratch. - invalid_grant: client_id mismatch. — Claude’s stored client_id isn’t in your DB anymore. Same fix as above (Reset OAuth State + delete/re-add the connector).
- invalid_request: Missing required parameters: … — something between Claude.ai and your PHP is stripping fields out of the POST body. Almost always a security plugin or host-level WAF. If this is your error, ask Xneelo support whether they have any WAF rules on /token and POST bodies — and confirm WP Ghost is fully uninstalled.
Send the View Details screenshot (or copy/paste) and we’ll route to the right fix from there.
Quick side note: Xneelo on Apache is a vanilla stack (no LiteSpeed, no Cloudflare in front), so I don’t expect cache eviction or trailing-slash issues — your environment looks clean from outside. The 5 candidates above are where it has to be.
RP Team
Hi,
Good news on both counts — what you’re asking for already exists, and there’s a better
path for Elementor specifically.The post-meta tools you’re describing (wp_get_post_meta, wp_update_post_meta,
wp_delete_post_meta) have been in Royal MCP for a while. You should already see them in
your Claude connector’s tool list — they expose every custom field on any post.That said, I’d strongly recommend NOT using them on _elementor_data directly. Elementor
stores pages as a deeply nested JSON tree where elements have random IDs, widget
settings are type-specific, and Elementor 4.0+ atomic widgets use an opaque schema
that’s unsafe to decode. Round-tripping that JSON through raw meta read/write will
break atomic widgets, duplicate element IDs (which breaks the editor), and mangle
escape sequences on save.Royal MCP 1.4.19 added six dedicated Elementor tools that handle all of that safely:
elementor_clone_page — duplicate an existing page with fresh element IDs and draft
status
elementor_replace_text — bulk text substitution across heading, button, text-editor,
and other text-bearing widgets
elementor_replace_image — image URL swap across image, image-box, background, and
gallery widget settings
elementor_get_page_outline — compact section/container hierarchy for AI to reason over
without burning the JSON budget
elementor_list_local_templates — enumerate saved templates from the Elementor Library
elementor_import_template — wrap Elementor’s official import API for safe template
ingestionThey auto-register when Elementor is active and are hidden otherwise. Make sure you’re
on Royal MCP 1.4.19 or later (the latest is 1.4.22) and Claude should see all six in
the tool list.The design intentionally avoids letting AI generate Elementor JSON from scratch —
clone-and-customize from a known-good source is far more reliable than trying to
construct widget JSON by hand. If you want to walk through a specific workflow, happy
to help.Hi @alexandregenesis — circling back. The fix shipped in 1.4.13 as promised,
plus follow-on fixes in 1.4.14 and 1.4.15 worth noting.1.4.13 (your specific fix): /register, /token, /authorize now send
Cache-Control: no-store, no-cache, must-revalidate, privateandPragma: no-cacheon every response. Should make the PowerBoost
cache-poisoning behavior you diagnosed structurally impossible.1.4.14: GET /wp-json/royal-mcp/v1/mcp (without auth) now returns
401 +WWW-Authenticate: Bearerinstead of 405 — restores Claude.ai web’s
RFC 9728 OAuth discovery probe.1.4.15: Same no-store headers extended to the /mcp endpoint and every response
under /wp-json/royal-mcp/* — 1.4.13 was a partial fix, /mcp itself was still
vulnerable to the same edge-cache poisoning pattern. Also flipped 403→401 for
invalid API key (RFC 7235 alignment, helps strict MCP clients fall back to
OAuth correctly).To verify after updating to 1.4.15:
curl -v -X POST "https://yoursite.com/register?_=$(date +%s)" \
-H "Content-Type: application/json" \
-d '{"client_name":"Test","redirect_uris":["https://claude.ai/api/mcp/auth_callback"],"grant_types":["authorization_code"],"response_types":["code"],"token_endpoint_auth_method":"none"}'Response headers should now include
Cache-Control: no-store, ...and the body
should be a 201 withclient_id. Then try the Claude.ai connector directly —
should complete OAuth without the cache excludes you configured as a workaround.If you still get a cached 405 even with the fresh query string, that means
PowerBoost is overridingCache-Control: no-storeon its own response
interception — a host-side bug worth escalating to o2switch. The
/register, /token, /authorize cache excludes would remain the permanent
solution in that case.Mind giving 1.4.15 a try and letting us know? If it works without the manual
cache excludes, please mark the topic as resolved — helps the next person on
a LiteSpeed/PowerBoost setup land on the right answer faster.— Royal Plugins team
Hi @michealdupont — thanks for the request, good thing to surface so we can
explain where we draw the line.
Short answer: PHP file editing isn’t on the roadmap and probably never will be,
and I want to give you the why so it doesn’t feel arbitrary.
The threat model. Every Royal MCP tool today operates on content that lives in
the database — posts, pages, products, taxonomy, comments, settings. All of
that is recoverable from a backup, and the worst-case scenario for a leaked API
key is “someone messes with your content.” Adding PHP file write turns that
into “someone plants a backdoor in functions.php and hides their tracks.” Same
credential, completely different blast radius. We’d need to assume every
install was one phished API key away from full code execution, and that’s not a
trade we want to make for a free WP.org plugin.WP-side constraints. A meaningful percentage of production installs define
DISALLOW_FILE_EDIT or DISALLOW_FILE_MODS in wp-config.php specifically to
prevent any plugin (or admin user) from editing PHP files. Honoring those
constants means the tool silently no-ops on those sites. Not honoring them
breaks WordPress best practice. Either way the feature is unreliable by design.Existing paths that already do this safely. WP-CLI over SSH (
wp eval-file,wp shell), an IDE with SFTP/SSH access, or a “Code Snippets”-style plugin
that stores PHP in a DB row with syntax validation and an instant kill switch.
Each of those has guardrails (atomic write, lint, rollback) that an MCP
file_put_contents tool would lack without a substantial engineering investment
that doesn’t fit our scope.For your specific case — hiding sidebars and going full-width — the CSS via
Customizer route the AI suggested is actually the cleanest answer regardless
of capabilities. It’s theme-update-safe, doesn’t need a child theme, and
reverts with one click if it ever causes layout issues.Hope that explains the stance, assuming since haven’t heard anything more on
this in over a week that the CSS approach worked, so marking as resolved for now.Hi @beerloft — circling back. Since 1.4.3 we’ve shipped 1.4.13, 1.4.14, and 1.4.15
with several fixes that line up with what you reported:• 1.4.13 — Added
Cache-Control: no-storeto OAuth endpoints (/register, /token,
/authorize). Pre-1.4.13, host-level edge caches (LiteSpeed, Cloudflare APO,
fastcgi cache) could pin an auth-error response and serve it back indefinitely
to every subsequent request.• 1.4.14 — GET /wp-json/royal-mcp/v1/mcp without auth now returns
401 + WWW-Authenticate: Bearer instead of 405. This is what claude.ai’s web
connector probes for to start its OAuth flow — pre-1.4.14 it would silently
fail with exactly the “Couldn’t reach the MCP server” message you saw, with
no authorization window ever appearing.• 1.4.15 — Same no-store headers extended to the /mcp endpoint itself and to
every response in the namespace (1.4.13 was a partial fix). Also flipped
invalid-API-key responses from 403 to 401 with a WWW-Authenticate header,
which is what RFC 9728 strict MCP clients need to fall back to OAuth
correctly.If you’re up for retesting:
1. Update both sites to 1.4.15. 2. Settings → Permalinks → Save Changes on each (forces a rewrite flush in case any /authorize routes weren't registered cleanly during prior upgrades). 3. If either site is behind Cloudflare, double-check that "Block AI Bots" / "Verified Bots" is OFF — Anthropic's backend (AS 399358) makes the server-to-server /register and /token calls, and Cloudflare blocks that ASN by default on new zones. This was the most likely cause of your original symptoms and we still see it bite people regularly. 4. Try connecting from claude.ai web again (since that was the failing path).If it works, would you mind marking the topic as resolved or comment back as resolved so I can? That helps the next person Googling the same symptoms land on the answer faster. If it still doesn’t, please share which scenario applies (A/B/C from my earlier reply) and any new error message and we’ll dig back in.
— Royal Plugins
Hi René,
Owe you an apology before anything else — I made a mistake in my very first reply that’s been quietly compounding through this entire thread. The SG Optimizer cache exclusion list I gave you was both wrong in one place and missing the most important endpoints. That alone could explain why nothing has stuck. Let me correct it and give you a definitive next step.
The exclusion list I gave you originally:
/wp-json/royal-mcp/*
/wp-json/royal-mcp/v1/oauth/* ← MISTAKE: this path has never existed in
Royal MCP
/.well-known/oauth-authorization-server
/.well-known/oauth-protected-resource
The /wp-json/royal-mcp/v1/oauth/* line does nothing. There’s no such REST namespace in the plugin — Royal MCP’s OAuth endpoints are root-level rewrite rules at /authorize, /token, and /register — and those three paths were never in your exclusion list. They’re the actual OAuth handshake endpoints, and they’re the ones SG’s edge cache is most likely poisoning. Anyone who got “Authorization with the MCP server failed” on SG without those exclusions was probably hitting a cached response from a stale GET
probe.
Step 1 — fix the SG Optimizer exclusion list
WP Admin → SG Optimizer → Caching → Dynamic Cache → Exclude URLs. Replace the list with:
/wp-json/royal-mcp/*
/.well-known/oauth-authorization-server
/.well-known/oauth-protected-resource
/authorize
/token
/register
Save, then click Purge SG Cache in the SG Optimizer toolbar. The purge is critical — without it any cached 4xx responses from your earlier test attempts (when GET /mcp was still returning 405 pre-1.4.14) will keep getting served regardless of the new exclusions.
Step 2 — two diagnostic curls (run AFTER the cache purge)
These will tell us if anything else is in the way:
(A) Content-Type on the metadata files — should be application/json
curl -sI https://terraexperty.com/.well-known/oauth-authorization-server | grep -i content-type
curl -sI https://terraexperty.com/.well-known/oauth-protected-resource | grep -i content-type
(B) Force a cache-busted POST to /register — should return 201 with a client_id
curl -v -X POST “https://terraexperty.com/register?=$(date +%s)” \ -H “Content-Type: application/json” \ -H “Cache-Control: no-cache” \ -d ‘{“client_name”:”Test”,”redirect_uris”:[“https://claude.ai/api/mcp/auth_callback” ],”grant_types”:[“authorization_code”],”response_types”:[“code”],”token_endpoint_auth
method”:”none”}’
Send back the output of both. If (A) shows text/plain, that’s a separate Content-Type
blocker (covered in step 3). If (B) returns 201 Created with a client_id, the OAuth
registration plumbing is working and you can retry the Claude.ai connection right
then.
Step 3 — retract my “Path A” SiteGround support ticket recommendation
In my last reply I told you to open a SiteGround ticket asking for an nginx MIME-type override on the /.well-known/ paths. Don’t bother — that path is now closed. SiteGround confirmed to another Royal MCP customer yesterday that they no longer make per-customer nginx changes since their migration to Google Cloud infrastructure. The escalation route I pointed you at has been removed.
If diagnostic (A) above shows text/plain, the working alternative is a free Cloudflare Transform Rule that overrides the response Content-Type at the edge — works around SG entirely:
https://royalplugins.com/support/royal-mcp/siteground-cloudflare-content-type-fix.html
Two-minute setup on Cloudflare’s free plan, no SG involvement needed. Walkthrough on
that page.
The .htaccess ForceType application/json advice I gave you earlier in the thread also doesn’t work on these paths — nginx serves /.well-known/ files directly without falling through to Apache, so .htaccess is silently ignored. Apologies for sending you down that road.
Step 4 — fallback if you just need it working today
If the goal is simply “get Royal MCP usable with Claude” and Claude Desktop is acceptable, you can sidestep OAuth entirely with the API-key bypass — works in two minutes, no /.well-known/, no consent screen, no Cloudflare:
https://royalplugins.com/support/royal-mcp/connect-claude-desktop-api-key.html
Quick version: paste this into your Claude Desktop config, substituting your API key from WP Admin → Royal MCP → Settings:{
"mcpServers": {
"terra-experty": {
"command": "npx",
"args": [
"-y",
"mcp-remote",
"https://terraexperty.com/wp-json/royal-mcp/v1/mcp",
"--header",
"X-Royal-MCP-API-Key: YOUR_KEY_HERE"
]
}
}
}Restart Claude Desktop. Connected.
Send back the curl output from step 2 once you’ve done step 1 and we’ll either be done or know exactly which layer to tackle next. Sorry again for the runaround on the wrong cache list — that should have been right from the start.
Best,
Jameson-Royal Plugins
Hi René,
I tested every endpoint on terraexperty.com from our side just now, and have a definitive picture for you. The good news: Royal MCP on your site is fully functional. Every OAuth endpoint reaches PHP correctly:
GET /.well-known/oauth-authorization-server → 200 ✅
GET /.well-known/oauth-protected-resource → 200 ✅
GET /wp-json/royal-mcp/v1/mcp → 401 ✅ (with WWW-Authenticate:
Bearer resource_metadata=”…”)
POST /register → 201 ✅ (dynamic client registration confirmed working — I just registered a test client and it succeeded)
GET /authorize → reaches PHP (X-Httpd: 1)The 1.4.14 fix is verified live. DCR works. The plugin is doing its job.
Two important corrections on what you’re seeing:
- Empty debug.log is GOOD news, not bad. WordPress’s debug.log only captures PHP
errors and notices, not request traffic. An empty debug.log during OAuth attempts
means no PHP errors were thrown — which is exactly what you want. It does NOT mean
Claude.ai’s requests aren’t reaching your server. Royal MCP also doesn’t write
OAuth handshake events to its own Activity Log (that log is for post-auth MCP tool
calls). To see actual request traffic during OAuth attempts, pull your nginx
access log from SiteGround Site Tools → Statistics → Access Log. - The most likely remaining blocker is Content-Type on your static files. Both
.well-known/ files are returning: Content-Type: text/plain RFC 8414 §3.2 requires OAuth discovery responses to be served as application/json (“a successful response MUST use the 200 OK HTTP status code and return a JSON object using the application/json content type”). Most clients are lenient and parse JSON regardless of Content-Type. Strict clients reject the metadata even
though the body is valid JSON. Another customer on SiteGround GrowBig hit this
same fingerprint recently — same host configuration, same text/plain, same OAuth
failure. That’s two production data points pointing at the same root cause. Two paths forward, depending on your use case:
Path A — Fix the Content-Type properly (works for Claude.ai web AND Claude Desktop) Open a SiteGround support ticket with this exact wording:
“We have two static files at /.well-known/oauth-authorization-server and /.well-known/oauth-protected-resource that are required by RFC 8414 (OAuth 2.0 Authorization Server Metadata). They are currently being served with Content-Type: text/plain because they have no file extension, but RFC 8414 §3.2 mandates Content-Type: application/json. .htaccess ForceType doesn’t apply since nginx serves these files directly without falling through to Apache. Could you please add an nginx-level MIME type override so files at these two specific paths are served as application/json? They contain valid JSON; only the Content-Type header needs correction.”
SiteGround has granted similar exceptions for other customers’ OAuth needs.
Path B — Skip OAuth entirely on Claude Desktop (works in 2 minutes, today)
If your goal is to get Royal MCP working with an AI assistant and Claude Desktop
is acceptable for your workflow, you can bypass the OAuth flow entirely. Royal MCP
supports API-key authentication via mcp-remote’s –header flag — no
/.well-known/, no discovery, no consent screen. Confirmed working on SiteGround
GrowBig.Full walkthrough:
https://royalplugins.com/support/royal-mcp/connect-claude-desktop-api-key.htmlQuick version — paste this into your Claude Desktop config
(claude_desktop_config.json), substituting your API key from WP Admin → Royal MCP
→ Settings:{
"mcpServers": {
"terra-experty": {
"command": "npx",
"args": [
"-y",
"mcp-remote",
"https://terraexperty.com/wp-json/royal-mcp/v1/mcp",
"--header",
"X-Royal-MCP-API-Key: YOUR_KEY_HERE"
]
}
}
}Restart Claude Desktop and you’re connected.
Path A is the proper fix and unblocks Claude.ai web. Path B works around the issue
today if waiting on SiteGround isn’t workable.Best,
Jameson– Royal Plugins
V.1.4.14 is live, test it and let me know
Hi René,
I see the issue now, will have update addressing this in next 24hrs V1.4.14 – be on the lookout
Hi René,
That diagnostic is gold — those green checkmarks prove the static-file workaround unblocked the host-level OAuth handshake. The “Unknown client_id” is the next layer down, and it points at Claude.ai’s
connector configuration on your side, not a Royal MCP bug.What’s happening:
Claude.ai’s MCP connector supports two modes:
- Automatic OAuth (the standard flow Royal MCP expects) — you give Claude only your MCP URL. Claude auto-discovers your OAuth metadata at /.well-known/oauth-authorization-server, calls /register to
dynamically register itself per RFC 7591, receives a fresh client_id, then sends the user to /authorize. Royal MCP recognizes the freshly-registered client_id and the handshake completes. - Custom OAuth credentials / Bearer Token — you pre-register a client and paste a client_id (or token) into Claude’s connector form manually. Claude skips /register entirely and sends whatever client_id you
entered straight to /authorize. If that ID doesn’t exist in Royal MCP’s clients table, you get exactly the error you’re seeing. Your first message mentioned “creating a custom connector with a Bearer Token” — that’s mode 2, and Claude.ai may still be using that cached configuration even if you’ve since pasted the URL alone. That
perfectly matches your observation that /register is never called and /authorize arrives with an unknown ID. The fix: - In Claude.ai → Settings → Connectors, fully remove the existing Royal MCP / Terra Experty connector. Don’t just edit it — remove it completely so Claude purges its cached config.
- Wait ~10 seconds.
- Click Add connector and select the standard “Add MCP server by URL” option (not the “Custom OAuth” / “Bearer Token” option).
- Paste only the MCP URL: https://terraexperty.com/wp-json/royal-mcp/v1/mcp — no client_id, no secret, nothing else.
- Click through the OAuth flow. Verify it’s now using DCR: While you click “Add connector”, tail your access log: tail -f /var/log/nginx/access.log | grep -E ‘(register|authorize|token)’ You should see POST /register HTTP/2 201, then GET /authorize …, then POST /token …. If /register does not appear, Claude is still running in the custom-credentials mode — back to step 1. About the empty Activity Log: Royal MCP’s Activity Log records MCP tool calls (post-auth), not the OAuth handshake itself. So an empty log during the connection attempt is expected behavior, not a separate signal of trouble — once you’re
connected and Claude makes its first tool call, you’ll see it land in the log. Try the remove-and-re-add and send back the access-log lines. We’re one click away from a working connector.
Best
Jameson