If you searched for how to secure a WordPress MCP server and landed on material talking
about OAuth 2.1 redirect URIs, client registration, or authorization code flows, that
material is not describing the plugin this track builds on. The MCP Adapter
(github.com/WordPress/mcp-adapter) has no OAuth implementation. What it actually uses
is WordPress’s existing authentication surface: Application Passwords for remote
clients, and cookie/nonce authentication for logged-in browser sessions. This lesson
walks through both, and why that’s genuinely a reasonable design, not a missing feature.
A working MCP server built with the MCP Adapter (Course 2), and at least one MCP client connected to it (Course 3). This lesson is conceptual, you won’t change any code yet.
Step 1: Where the OAuth assumption comes from
WordPress.com offers a separate, proprietary, hosted MCP product for its own platform, and that product does use OAuth. It is a different codebase, a different vendor relationship, and out of scope for anything self-hosted. The open-source MCP Adapter plugin, the one you install on your own WordPress site and the one every other lesson in this track uses, was never built with an OAuth flow. Confusing the two leads people to go looking for a client ID, a client secret, and a redirect URI setting that does not exist in this plugin, and to conclude (wrongly) that the adapter’s security model is somehow behind other MCP server implementations. It isn’t behind, it’s just built on a different, already-existing WordPress mechanism.
If you are evaluating WordPress.com’s hosted MCP offering specifically, its OAuth model is a legitimate thing to research on its own terms. But if you’re running the self-hosted MCP Adapter plugin covered in this track, there is no OAuth surface to configure, secure, or audit. Don’t spend time hardening a flow that isn’t there.
Step 2: Application Passwords, the real remote auth mechanism
For any MCP client connecting over HTTP (Claude Desktop’s remote connector, Cursor, a custom script), the adapter relies on WordPress core’s built-in Application Passwords feature, available since WordPress 5.6. An application password is a randomly generated credential tied to one specific WordPress user account. The client sends it as HTTP Basic Auth:
Authorization: Basic base64(username:xxxx xxxx xxxx xxxx xxxx xxxx)
WordPress core resolves that header to a real, existing WP_User, exactly the user who
generated the password. There is no separate “MCP identity”, the agent is authenticating
as a specific WordPress user, with that user’s exact roles and capabilities. This single
fact is the foundation for the entire permissions module later in this course.
Basic Auth sends credentials in a reversible, base64-encoded form on every request. Over plain HTTP this is trivially interceptable. WordPress core will refuse to expose the Application Passwords UI at all on a non-HTTPS site (localhost is exempted for development). Never point a real AI client at an HTTP-only WordPress install.
Step 3: Cookie/nonce auth for browser-based sessions
Not every MCP interaction is a separate remote client. If your setup involves a
browser-based tool acting on behalf of a user who is already logged into wp-admin, the
adapter can rely on WordPress’s existing cookie authentication plus a nonce, the same
mechanism the core REST API has used for years. The request carries the browser’s
authentication cookies and an X-WP-Nonce header instead of an Authorization header.
This path only makes sense for same-origin, browser-context tools, not for a
desktop AI client talking to a remote site, that’s what Application Passwords are for.
Step 4: The transport-level gate every request passes through first
Before any individual ability’s own permission_callback runs, the MCP Adapter checks a
server-wide transport permission callback. Unless you override it, that default is:
is_user_logged_in()
In practice this means: whichever authentication method resolved the request (an
Application Password’s Basic Auth, or a cookie/nonce pair) must have produced a real,
logged-in WordPress user, or the request is rejected before any tool is even considered.
Course docs (docs/guides/transport-permissions.md in the adapter repository) cover how
to replace this default with your own logic, which the permissions module later in this
course builds on directly.
Test it: confirm which mechanism your connection is actually using
On your existing MCP server from Course 2, check which header your connected client is actually sending. A quick way, temporarily log the incoming authorization state from inside a transport permission callback:
// File: my-plugin.php
add_filter( 'mcp_adapter_default_server_config', function ( $config ) {
$config['transport_permission_callback'] = function ( \WP_REST_Request $request ) {
error_log( 'MCP auth header present: ' . ( $request->get_header( 'authorization' ) ? 'yes' : 'no' ) );
error_log( 'MCP current user: ' . get_current_user_id() );
return is_user_logged_in();
};
return $config;
} );
Make a request from your connected AI client, then check your site’s error log. You should see a real, non-zero user ID, confirming the request resolved to an actual WordPress account, not an anonymous or synthetic one.
Passing the transport-level is_user_logged_in() check only proves the request is tied
to some real WordPress user. It says nothing about what that user is allowed to do.
That’s the job of each ability’s own permission_callback, covered starting in the next
module. Authentication and authorization are two separate questions, and this course
treats them that way throughout.
Recap
The MCP Adapter has no OAuth flow, remote clients authenticate with WordPress
Application Passwords over HTTPS, and browser-context tools can use cookie/nonce auth.
Every request also passes a transport-level permission check first, is_user_logged_in()
by default. The identity behind any MCP connection is always a real WordPress user
account, which is exactly why the next lesson focuses on how you generate, name, and
rotate those Application Passwords deliberately, rather than treating them as an
afterthought.
Resources & further reading
- MCP Adapter repository, GitHub
- Transport permissions guide, GitHub
- Application Passwords, WordPress core feature, make.wordpress.org