Skip to content

Auth errors (401 / 403)

The most common error. WordPress rejected your credentials. Walk through these:

  1. WordPress admin → Users → Profile → scroll to Application Passwords
  2. The password you created must be active (no revoked indicator)
  3. Application Passwords are formatted as xxxx xxxx xxxx xxxx xxxx xxxx, four-character chunks separated by single spaces. Don’t strip the spaces.
  4. The user the password belongs to must have at least edit_posts. Editor / Author / Administrator roles all qualify. Subscriber does not.

The Authorization header value should be Basic <base64-of-username:password>. Generate:

Terminal window
echo -n "admin:xxxx xxxx xxxx xxxx xxxx xxxx" | base64

Common 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=
Terminal window
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. The MCP endpoint should accept them too.

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.

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.

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-server

    A 200 with JSON means discovery works. A 403/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.

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.

If EMCP Tools → Connection doesn’t offer OAuth at all, the site isn’t on HTTPS (the only requirement to enable it). Application Passwords remain available regardless.

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:

  1. Call initialize. Read the Mcp-Session-Id response header
  2. 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).

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.