Auth errors (401 / 403)
401 Unauthorized
Section titled “401 Unauthorized”The most common error. WordPress rejected your credentials. Walk through these:
Check the Application Password
Section titled “Check the Application Password”- WordPress admin → Users → Profile → scroll to Application Passwords
- The password you created must be active (no revoked indicator)
- Application Passwords are formatted as
xxxx xxxx xxxx xxxx xxxx xxxx, four-character chunks separated by single spaces. Don’t strip the spaces. - The user the password belongs to must have at least
edit_posts. Editor / Author / Administrator roles all qualify. Subscriber does not.
Check the base64 encoding
Section titled “Check the base64 encoding”The Authorization header value should be Basic <base64-of-username:password>. Generate:
echo -n "admin:xxxx xxxx xxxx xxxx xxxx xxxx" | base64Common mistakes:
- Trailing newline. Use
echo -n(no newline). On Windows PowerShell:[Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes("admin:xxxx xxxx ...")) - URL-encoding the colon. Don’t. It’s part of the credential string, not a URL part.
- Wrapping the base64 in quotes inside the JSON. Quotes are JSON syntax; they shouldn’t be inside the header value.
The header should look exactly like:
Authorization: Basic YWRtaW46eHh4eCB4eHh4IHh4eHggeHh4eCB4eHh4IHh4eHg=Test with curl
Section titled “Test with curl”curl -i https://your-site.test/wp-json/wp/v2/users/me \ -u "admin:xxxx xxxx xxxx xxxx xxxx xxxx"If this returns a 401, your credentials are wrong (or WordPress isn’t accepting App Passwords on this install). If it returns a 200 with your user data, credentials work, and the MCP endpoint should accept them too.
The Connection screen tests the real MCP endpoint rather than only /wp/v2/users/me. On EMCP Tools → Connection, step 4 (“Test the connection”) waits for your client’s first call. If the call fails, it shows “We saw a call from client, but it failed” with the reason; if no call arrives, it shows “No call yet”. In both cases a Run a server test button appears: for an Application Password it runs the MCP handshake (initialize, notifications/initialized, tools/list) with the password you entered. If this curl succeeds but the server test fails, trust the server test: something past basic auth (a security plugin, a proxy, a host that strips the Authorization header) is interfering with the MCP endpoint specifically.
EMCP Tools → MCP Log also records requests that reached WordPress. Filter by Errors and use Show more on a row to see its client, credential, stage and failure reason. A request refused for bad credentials is still logged, with less detail. Requests with no signed-in user are logged at most once every 30 seconds per address, so a burst of failed attempts shows as one row. If nothing appears at all, the request most likely never reached WordPress. See MCP Log for what each field means.
App Passwords disabled?
Section titled “App Passwords disabled?”Some security plugins (Wordfence, iThemes) disable Application Passwords by default. Check Settings → General or your security plugin’s settings.
You can also test programmatically: var_dump( wp_is_application_passwords_available() ) in a snippet. Returns true if available.
403 Forbidden
Section titled “403 Forbidden”The credentials worked but the user doesn’t have permission for the specific tool. EMCP Tools enforces capabilities per tool:
- Read tools require
edit_posts - Write tools require
edit_posts+ ownership check on the target post - Global settings require
manage_options - Delete operations require
delete_posts - Code-injection tools require
unfiltered_html
Use an Administrator account during initial testing. Subscriber / Customer accounts will hit 403 on almost everything.
OAuth sign-in fails
Section titled “OAuth sign-in fails”If you’re using OAuth sign-in (approving a browser prompt instead of an Application Password) and the connection never completes:
No browser prompt / “discovery failed” / 404
Section titled “No browser prompt / “discovery failed” / 404”The client discovers the server by requesting /.well-known/oauth-protected-resource (RFC 9728). Two common causes:
-
Not HTTPS. OAuth is only offered on
https://sites. On HTTP, use an Application Password. -
Host blocks
/.well-known/. Some managed hosts and nginx/openresty defaults deny every/.well-known/path except ACME challenges, at the web-server layer, before WordPress runs. Test it:Terminal window curl -i https://your-site.com/.well-known/oauth-protected-resource/wp-json/mcp/emcp-tools-serverA
200with JSON means discovery works. A403/404(especially an nginx/hosting page, not WordPress) means the host is blocking it. Ask your host to allow/.well-known/requests, or use an Application Password instead, it doesn’t rely on discovery. -
Cloudflare (or another CDN/WAF) is blocking the client, not your browser. If your client reports “Couldn’t register”, or the connection just never completes, the most common cause is a CDN or security layer in front of the site (most often Cloudflare’s bot-fight mode) blocking the client’s server-side calls to the discovery and dynamic-registration endpoints. Your own browser reaches the site fine (it isn’t flagged as a bot), which is why this one is confusing: everything looks fine when you check it yourself.
Fix it by allow-listing these exact paths in the CDN/WAF:
/.well-known/oauth-authorization-server/.well-known/oauth-protected-resource/wp-json/emcp-tools/oauth/*/wp-json/mcp/emcp-tools-serverIf you can’t change the CDN/WAF config, fall back to an Application Password instead: on EMCP Tools → Connection, pick Application password in step 2, “Pick how it signs in”.
-
A competing OAuth plugin owns the shared
/.well-known/path. Some hosts and other WordPress OAuth plugins also serve/.well-known/oauth-authorization-server. As of v3.14.2, both discovery documents also have same-origin REST aliases that don’t compete for the shared path:/wp-json/emcp-tools/oauth/v1/protected-resource/wp-json/emcp-tools/oauth/v1/authorization-serverCurl these directly to confirm EMCP’s own document is reachable even when the bare
/.well-known/path is intercepted by something else. For OAuth, the Connection screen’s Run a server test (step 4) runs an OAuth discovery test. It compares both the HTTP status and the metadata’s identity, so it reports a clear error rather than a false green result when a different plugin’s valid-looking document is what’s actually being served at the shared path.
”Invalid client or redirect URI”: two different causes, two different fixes
Section titled “”Invalid client or redirect URI”: two different causes, two different fixes”As of v3.13.0 the error page tells you exactly which of these two happened, showing the requested and registered addresses side by side, so start there. Both produce the same headline error text, but the cause and the fix are unrelated.
“This site does not recognise the app making this connection request.” A connected client (Claude Desktop, ChatGPT, or another MCP client) periodically pops open a sign-in page on its own, and it never reconnects no matter how many times you approve it. This was a bug, fixed in v3.12.3: a cleanup routine deleted any client registration older than a day with no active tokens, intending to prune abandoned sign-in attempts, but a client whose tokens had simply lapsed (a 30-day-idle refresh token, or anything else that cleared them) also has no tokens, so its registration was deleted while the app still had it saved.
Fix: update to v3.12.3 or later. A client that has completed sign-in at least once is now kept permanently; only registrations that never completed sign-in are cleaned up, and this applies automatically to connections that already existed before you update. If you still hit this after updating, remove the connector from your AI client and add it again to start a fresh registration.
“The return address this app asked for does not match the one it registered.” A command-line MCP client (Codex or similar) that listens on your own machine can’t finish signing in. This was also a bug, fixed in v3.13.0: a CLI client listens on a fresh port each run and can spell “this machine” three ways (localhost, 127.0.0.1, ::1), but the check required an exact string match, so a client that registered one spelling and came back with another (or with a trailing slash added or dropped) was rejected even though both addresses point at the same place.
Fix: update to v3.13.0 or later. All three loopback spellings are now treated as equivalent and a trailing slash is ignored, for http loopback addresses only. Every https redirect URI still has to match exactly; that part hasn’t loosened. Reconnect the client after updating.
Token rejected right after updating to v3.14.2
Section titled “Token rejected right after updating to v3.14.2”As of v3.14.2, OAuth tokens are bound to the specific MCP resource they were issued for (RFC 8707), checked on every bearer request. Existing tokens are migrated automatically to the plugin’s one canonical MCP resource on update, so this shouldn’t require reconnecting. If a client is rejected anyway, disconnect and reconnect it once to get a token bound to the current resource. On EMCP Tools → Connection, the Connected apps list in the right-hand panel shows each OAuth app with its status and Sign out and Remove actions; in step 3 you can choose “Reconnect an app that is already connected”.
”Only administrators can approve” / consent denied
Section titled “”Only administrators can approve” / consent denied”The consent screen refuses non-admin users. Sign in to WordPress as an administrator in the same browser before approving.
OAuth option missing or greyed out
Section titled “OAuth option missing or greyed out”On EMCP Tools → Connection, the OAuth sign-in method in step 2 needs two things: HTTPS, and the OAuth sign-in switch turned on under Advanced settings in the right-hand panel (then Save). When it is off, the OAuth card reads “Turn on OAuth sign-in in Advanced settings.” On a site without HTTPS the switch itself is disabled with “Needs HTTPS on this site.” Application Passwords remain available regardless.
Session errors (Mcp-Session-Id required)
Section titled “Session errors (Mcp-Session-Id required)”WordPress’s MCP Adapter requires the Mcp-Session-Id header on every request after the initial initialize call. The session ID is in the response headers of initialize. Subsequent requests must include it.
If you’re testing manually with curl:
- Call
initialize. Read theMcp-Session-Idresponse header - Include that exact header value on every subsequent request (
tools/list,tools/call, etc.)
Most MCP clients handle this automatically. For clients that don’t, the Node.js proxy handles it. Run it with npx -y @msrbuilds/emcp-proxy@latest (no local file to maintain).
CORS / browser-only issues
Section titled “CORS / browser-only issues”The REST endpoint isn’t designed to be called from browser JavaScript directly. It requires either the application password (server-side) or a logged-in nonce (browser-side via wp_localize_script). If you’re getting CORS errors, you’re probably calling it from the wrong context.
For programmatic browser access, the WordPress REST API’s nonce auth is the right path, but no MCP client does this. They all run server-side or as native apps with proper credential handling.
