# MCP / OAuth Clients Getting Locked Out by GuardPress

> Royal MCP, Claude.ai, n8n or Make.com clients getting 403 or rate-limited by GuardPress: enable the MCP / OAuth carve-out (1.6.35+) to exempt the REST paths.

- Canonical: <https://royalplugins.com/support/guardpress/mcp-oauth-apps-locked-out/>
- HTML version: <https://royalplugins.com/support/guardpress/mcp-oauth-apps-locked-out/>

---

You connected Royal MCP (or another MCP server) to your WordPress site, pointed Claude.ai / Apify / n8n / Make.com / the Claude Code CLI at it, and within a minute or two the agent gets blocked — 403s at the WAF, a brute-force lockout on the IP, or a rate-limit reject after a flurry of tool calls. This is GuardPress doing its job slightly too aggressively for agent traffic. The fix is a single checkbox: **Allow MCP / Agent Traffic**, added in GuardPress 1.6.35 and extended in 1.6.36, which carves a narrow set of MCP and OAuth paths out of three checks that conflict with agent runtimes. This runbook covers the symptom, what the carve-out exempts (and what it doesn’t), how to enable it, and how to verify it’s actually doing what you think.

### TL;DR — the fix is one checkbox

Update GuardPress to 1.6.35 or higher, then go to **GuardPress → Settings → Firewall & Protection** and check **“Allow MCP / Agent Traffic”**. Save. Try your agent connection again. If it still fails, jump to [Still getting blocked?](#still-blocked) for the troubleshooting flow.

## The Symptom

Three flavours, all caused by the same underlying mismatch — GuardPress’s pre-1.6.35 defenses see agent traffic and think it’s an attack:

- **Connection 403’d at the WAF immediately.** Claude.ai or Apify tries to reach your `/.well-known/oauth-authorization-server` or `/wp-json/royal-mcp/v1/...` and the request bounces with a 403 before any auth even happens. GuardPress logs *“Malicious bot detected”* or similar in the audit log.
- **IP gets locked out after a few token-exchange retries.** The MCP client successfully authenticates the first session, then on session 2 or 3 hits a 403 with *“Too many failed login attempts. Please try again in N minutes.”* — even though no login was actually attempted on wp-login.php. The OAuth `/token` exchange (or an Application Password verification that briefly fails) fed the brute-force counter, the counter hit the threshold (default 5 in 15 minutes), and the IP got blocked.
- **Rate-limit reject mid-conversation.** A long Claude chat or an Apify Actor fires 60+ tool calls per minute, GuardPress’s rate limiter (if enabled, default 60/min) catches the IP, and the rest of the agent session 403’s for 5 minutes. Then it works again. Then it fails again.

All three symptoms can hit the same client in the same session — they’re separate checks at different layers (WAF, brute-force, rate limit), each independently configured.

## Why This Happens

The three checks above are correct for their original use case (humans signing in via a browser) and wrong for the agent / MCP use case (programmatic, high-frequency, short user-agents). Specifically:

- **Suspicious-UA check (WAF).** GuardPress’s WAF rejects requests with a user-agent shorter than 10 characters as “suspicious bot.” Modern agent runtimes ship bare UAs like `node`, `axios`, `python-httpx`, `node-fetch`, and `claude-code` — all under 10 chars, all instantly rejected.
- **Brute-force counter.** GuardPress counts both `wp_login_failed` events *and* WP’s `application_password_failed_authentication` hook (added in 1.6.4 to throttle REST brute-force). OAuth `/token` exchanges and Royal MCP’s `Token_Store` auth-code exchange can call `wp_authenticate_application_password()` on some configurations — if that fails even once (e.g. a stale refresh token), it counts as a failed login. A handful of retries during a normal OAuth flow trips the counter and locks the IP.
- **Per-IP rate limit.** GuardPress’s per-IP rate limiter is sensible for human traffic. For an agent running dozens of tool calls per minute against your site, it’s instantly tripped — even though Royal MCP has its own per-token rate limiter that’s already throttling correctly. Two rate limiters on the same traffic is one too many.

The fix isn’t to weaken any of those checks site-wide — that would let real attackers through. The fix is to scope an exemption to the specific URL paths MCP and OAuth use, and only when an admin has explicitly opted in.

## The MCP / OAuth Carve-Out (1.6.35 + 1.6.36)

GuardPress 1.6.35 introduced a master toggle named **Allow MCP / Agent Traffic** (stored in the `guardpress_allow_mcp_traffic` option, default OFF). When enabled, GuardPress matches the current request path against a known set of MCP / OAuth paths and, if it’s a match, skips:

- The bare-UA “suspicious” check in the WAF (1.6.35)
- The brute-force counter check on `authenticate` (1.6.35)
- The brute-force counter feed on `application_password_failed_authentication` (1.6.35)
- The per-IP rate limiter (added in 1.6.36)

The carve-out covers these paths:

- `/wp-json/royal-mcp/v1/*` — Royal MCP’s own REST namespace (pretty permalink form)
- `?rest_route=/royal-mcp/...` — same, plain-permalink form
- `/wp-json/<ns>/mcp` — generic MCP REST surface used by third-party MCP servers
- `?rest_route=<ns>/mcp` — same, plain-permalink form
- `/.well-known/oauth-authorization-server` — OAuth metadata discovery
- `/.well-known/oauth-protected-resource` — OAuth protected-resource discovery
- `/authorize`, `/token`, `/register` — top-level OAuth endpoints registered by Royal MCP’s rewrites

What the carve-out does NOT exempt

SQL injection patterns, XSS attempts, path traversal, named-scanner blocks (sqlmap, nikto, etc.), real attack payloads in user-agents (`<script>`, `eval(`) and any non-MCP / non-OAuth path are still fully protected. The carve-out is narrow on purpose — it only relaxes the three checks above, only on the specific path patterns above, and only when an admin has explicitly enabled the master toggle.

## How to Enable the Carve-Out

#### Confirm you’re on GuardPress 1.6.35 or higher

Check **WP Admin → Plugins**. If you’re on 1.6.34 or earlier, update first — the toggle doesn’t exist before 1.6.35. For full coverage (including the per-IP rate-limit exemption) you want 1.6.36 or higher.

#### Open Firewall & Protection settings

Go to **GuardPress → Settings**, scroll to the **Firewall & Protection** section.

#### Check “Allow MCP / Agent Traffic”

Just below the “Web Application Firewall” row you’ll see **Allow MCP / Agent Traffic** with the description: *“Permit MCP servers and AI agents to reach OAuth and REST endpoints.”* The expanded help text lists the exact paths and exempted checks. Check the box.

![GuardPress Firewall and Protection settings section. Top row: Web Application Firewall with Enable WAF protection checkbox checked and a green RECOMMENDED badge. Below it: Allow MCP / Agent Traffic with Permit MCP servers and AI agents to reach OAuth and REST endpoints checkbox checked, followed by expanded help text explaining when to enable, the bare-UA reject + brute-force counter + per-IP rate limit checks that get skipped, and the carve-out path list: /wp-json/royal-mcp/v1/*, /wp-json/<ns>/mcp, /.well-known/oauth-authorization-server, /.well-known/oauth-protected-resource, /authorize, /token, /register. Footer note: SQL/XSS/path-traversal checks and named-scanner blocks still apply to MCP paths even when this is on.](https://royalplugins.com/support/guardpress/images/guardpress-allow-mcp-traffic.png)

#### Save and retry your agent connection

Save the settings page. The change is immediate — the very next request from your MCP client to one of the carve-out paths will skip the three checks. There’s no cache to clear and no daemon to restart.

## How to Verify the Carve-Out Is Active

Three things to check after enabling:

### 1. The checkbox itself

Re-open **GuardPress → Settings → Firewall & Protection** and confirm **Allow MCP / Agent Traffic** is still checked. If a different admin or a settings-management plugin (Solid Security, etc.) flips it back off, you’ll see it here.

### 2. The agent connects successfully

Run a sample MCP query from your agent (Claude.ai prompt that uses a tool, an Apify Actor with a single API call, an n8n workflow with one node). It should succeed without any 403 / lockout / rate-limit error. Run a few in quick succession to confirm the rate-limit exemption is working too.

### 3. The GuardPress audit log is quiet

Open **GuardPress → Audit Log**. After the toggle is on, you should no longer see `ip_blocked_brute_force` entries triggered by your MCP client’s IP, no *“Malicious bot detected”* entries for the agent runtime UAs, and no rate-limit blocks. If those entries are still appearing for paths matching the carve-out list, something else is going on — see the troubleshooting section below.

## Still Getting Blocked? Troubleshooting Flow

### Confirm the request is hitting an MCP / OAuth path

The carve-out is path-scoped. If your agent is hitting some other endpoint — `/wp-admin/admin-ajax.php`, a custom REST namespace that doesn’t contain “mcp” in the URL, or wp-login.php directly — the carve-out doesn’t apply and the request goes through all the normal checks. Tail your server access log during the agent session and confirm the URL pattern actually matches one of the paths in the [covered paths list](#carveout).

### Confirm your IP isn’t already in the blocked list

If your IP got blocked *before* you enabled the carve-out, the block is still active — the toggle only prevents future blocks; it doesn’t retroactively clear existing ones. Go to **GuardPress → IP Management**, scroll to the **Blocked IPs** section, find your client’s IP, and click **Unblock**.

### Whitelist the MCP client IP as a belt-and-braces move

For known-good agent IPs (a static Apify Actor IP, your own home IP for Claude Code CLI, a cloud function’s outbound IP), you can also whitelist them under **GuardPress → IP Management → IP Whitelist**. Whitelisted IPs bypass all firewall checks — the WAF, brute-force counter, and rate limiter all skip a request from a whitelisted IP regardless of path. CIDR ranges are supported (e.g. `192.0.2.0/24`). Don’t whitelist anything you don’t fully trust — this is broader than the carve-out by design.

### Check your custom MCP namespace is detected

The generic third-party-MCP detector matches paths shaped like `/wp-json/<namespace>/mcp`. If your MCP server uses a different shape (e.g. `/wp-json/myco-mcp/agents` with no literal `/mcp` segment), the built-in detector won’t match it. There’s a filter for this: `guardpress_is_mcp_friendly_path`. In a small mu-plugin or your theme’s functions.php:

```
add_filter( 'guardpress_is_mcp_friendly_path', function( $matched, $path ) {
    if ( strpos( $path, '/wp-json/myco-mcp/' ) === 0 ) {
        return true;
    }
    return $matched;
}, 10, 2 );
```

This extends the carve-out to your custom namespace. The filter receives the boolean detector result and the normalised lowercase request path.

### Check Application Password configuration

If you’re using WP Application Passwords as the bearer-credential layer (rather than full OAuth), make sure the Application Password was created via **Users → [your user] → Application Passwords** and you’re sending the username + the generated password as HTTP Basic Auth. A 401 on a correct password usually means the username doesn’t match or the password got mangled in transit (extra spaces, missing dashes). A 401 on every attempt — even ones that look right — can also mean WP’s Application Passwords are disabled at the wp-config / filter level (`wp_is_application_passwords_available` returning false).

### Check that GuardPress sees the real client IP

If your site sits behind Cloudflare, a load balancer, or any other reverse proxy, the IP GuardPress sees by default is the proxy’s IP — not your agent’s. GuardPress’s IP detector honours `HTTP_CF_CONNECTING_IP`, `HTTP_X_FORWARDED_FOR`, `HTTP_X_REAL_IP`, and `HTTP_CLIENT_IP` when present. If headers aren’t configured correctly, brute-force blocks land on the proxy IP — which then blocks every legitimate visitor coming through that proxy, not just your agent. See [**Country Blocking & Cloudflare Real-IP**](https://royalplugins.com/support/guardpress/country-blocking-cloudflare-real-ip/) (covers the same misconfiguration class).

### Confirm 2FA isn’t interfering

WP Application Passwords intentionally bypass interactive 2FA (a bearer token *is* the second factor) and GuardPress’s per-role 2FA enforcement only runs on `admin_init` — not on REST. If your MCP client’s user has 2FA enrolled, REST / Application Password auth still works. If it’s failing with a 401 specifically on the OAuth-callback redirect to wp-admin (rare but happens with browser-based authorization flows), see [**Per-Role 2FA Enforcement Setup**](https://royalplugins.com/support/guardpress/per-role-2fa-enforcement-setup/).

## Application Passwords + GuardPress: How They Intersect

WordPress 5.6+ ships native Application Passwords — per-user bearer credentials that the REST API can authenticate with via HTTP Basic Auth. Royal MCP’s `Token_Store` uses this surface for some configurations, and most third-party MCP servers eventually fall back to it. Two places GuardPress and Application Passwords intersect:

- **Failed Application Password auth feeds the brute-force counter** via WP’s `application_password_failed_authentication` action (GuardPress hooks this in 1.6.4 to close the REST brute-force gap). With the MCP carve-out OFF, every fumbled Application Password attempt against a carved-out path increments the IP’s failed-login count — 5 of those in 15 minutes (or whatever your max-attempts setting is) and the IP gets blocked. With the carve-out ON, failed Application Password auth on MCP / OAuth paths is silently ignored by the brute-force counter.
- **Users with 2FA enabled can’t create new Application Passwords** via the GuardPress filter on `wp_is_application_passwords_available_for_user` — this is a 2FA-policy decision, not a carve-out concern. Existing Application Passwords still work for those users. If you need to create a new Application Password for an MCP client, do it as a user without 2FA enrolled, or temporarily disable 2FA for that user, create the password, then re-enable.

## Common Pitfalls

### Enabling the carve-out without updating to 1.6.36 for rate-limit coverage

If you’re on 1.6.35 exactly, the carve-out covers the bare-UA reject and the brute-force counter but *not* per-IP rate limiting — that exemption shipped in 1.6.36. A long agent session can still trip the rate limiter on 1.6.35. Update to 1.6.36+ for the full carve-out.

### Cloudflare real-IP misconfiguration making the carve-out look broken

Symptom: the carve-out is enabled, your MCP client’s real IP is whitelisted, but you’re still seeing brute-force blocks on a completely different IP — one of Cloudflare’s edge IPs. The real-IP header isn’t being honoured (or Cloudflare isn’t set up to send it), so GuardPress is blocking the proxy not the client. Fix the real-IP detection first; the carve-out is correct, it’s just blocking the wrong IP. See the Country Blocking & Cloudflare Real-IP article linked above.

### Treating “Allow MCP” as “Allow Anything REST”

The toggle is narrow. `/wp-json/wp/v2/posts` isn’t MCP — it’s the core REST API. Hitting it from an agent with a bare UA will still get the suspicious-UA reject. If you have an agent integration that uses the core REST API directly (rather than an MCP server in front of it), whitelist the IP instead.

### Forgetting the carve-out applies to GuardPress, not WP core

The carve-out exempts *GuardPress’s* checks. WordPress core still applies its own auth checks (capabilities, nonces, REST permission callbacks), and any other security plugin you have installed (Wordfence, Solid Security, etc.) applies its own. If you have multiple security plugins, you need to configure each one for MCP traffic separately.

### Custom login URL not exempted automatically

If you’ve set a Custom Login URL under **GuardPress → Settings → Login Security**, your wp-login.php is hidden behind a custom slug. Agent traffic doesn’t normally hit wp-login.php (it goes to `/token` / Application Password endpoints instead), so this usually doesn’t matter. But if you have an MCP client that does need to walk a browser OAuth flow through your custom login URL, that path is *not* in the carve-out — it’s a wp-login surface and stays gated.

## FAQ

### Why isn’t the carve-out on by default?

Default-OFF was an explicit choice for the 1.6.35 rollout: existing installs see zero behaviour change on upgrade, and the carve-out only takes effect when an admin opts in. Sites that don’t run an MCP server or use agent traffic at all get no surface-area increase from the feature shipping.

### Does the carve-out weaken security on the OAuth surface?

It relaxes three specific checks (bare-UA reject, brute-force counter, per-IP rate limit) on a narrow list of paths. SQL/XSS/path-traversal pattern checks, named-scanner blocks (sqlmap, nikto, etc.), real-attack-payload-in-UA checks (`<script>`, `eval(`), and WordPress core’s own auth (capabilities, nonces, signature verification on tokens) all still apply. Royal MCP also enforces its own per-token rate limiter independent of GuardPress — double-throttling at the per-IP level was the original problem, not a security feature.

### Can I extend the carve-out to my own custom MCP namespace?

Yes — hook the `guardpress_is_mcp_friendly_path` filter (example in the troubleshooting section above). The filter receives the detector’s default boolean result plus the normalised request path, so you can extend without disabling the built-in matches.

### Why am I still hitting 403s on first connect even with the carve-out enabled?

If first-connect is failing but the toggle is on, check three things in order: (1) is the request actually hitting one of the covered paths — tail your access log; (2) is your IP already in the blocked list from earlier failed attempts — check **GuardPress → IP Management → Blocked IPs** and unblock; (3) is GuardPress seeing your real IP or your reverse proxy’s — check the Cloudflare real-IP linked article. Most “still blocked after enabling the toggle” cases are one of those three.

### Does this work with WordPress.com or other managed hosts?

The toggle is a normal WP option (`guardpress_allow_mcp_traffic`), set via the GuardPress settings UI — nothing host-specific. Where managed hosts get involved is at their own edge (their own WAF, their own rate limiter), which sits in front of GuardPress and is invisible to it. If your agent gets through GuardPress but still hits a host-level 403, that’s a host configuration question — contact your host’s support with the request details.

## Still Stuck? Email Priority Support

If the carve-out is enabled, your IP is unblocked, and you’ve confirmed the request path matches one of the covered patterns but the agent is still being blocked:

Email **[support@royalplugins.com](mailto:support@royalplugins.com)** with the diagnostic info below. Priority email support is included with your GuardPress Pro license — typical response time is within 24 hours. Royal MCP / agent-integration issues we treat as high priority since they often block paid work.

#### Information to include in your email

- **GuardPress version** from **WP Admin → Plugins** (must be 1.6.35 or higher for the carve-out; 1.6.36+ for rate-limit coverage)
- **WordPress version** from **WP Admin → Updates**
- **Which MCP / agent client** — Claude.ai, Claude Code CLI, Apify, n8n, Make.com, custom
- **The exact URL path** the client is hitting when it gets blocked (from your server access log or the client’s error message)
- **The user-agent** the client is sending (also from access log)
- **The exact error response** the client receives (status code + body)
- **The most recent matching entries from GuardPress → Audit Log** — look for `ip_blocked_brute_force`, “Malicious bot detected”, or rate-limit entries with timestamps close to the agent failure
- **Whether your site is behind Cloudflare or another reverse proxy** — this changes which IP GuardPress is looking at
- **Confirmation that “Allow MCP / Agent Traffic” is checked** in **Settings → Firewall & Protection** — this is the #1 first question we’ll ask anyway

Related GuardPress topics

If your issue isn’t MCP/OAuth-specific:

- [**Country Blocking & Cloudflare Real-IP**](https://royalplugins.com/support/guardpress/country-blocking-cloudflare-real-ip/) — when GuardPress is blocking the wrong IP because real-IP detection isn’t configured
- [**Per-Role 2FA Enforcement Setup**](https://royalplugins.com/support/guardpress/per-role-2fa-enforcement-setup/) — require 2FA for selected roles without breaking REST / Application Password access
- [**Emergency Lockdown Recovery**](https://royalplugins.com/support/guardpress/emergency-lockdown-recovery/) — if Emergency Lockdown is also active, MCP clients are rejected at the authenticate filter regardless of the carve-out
- [**Locked Out Recovery**](https://royalplugins.com/blog/wordpress-locked-out-recovery/) — broader lockout recovery covering brute-force, IP blocks, and custom login URL gotchas

[Back to GuardPress Support](https://royalplugins.com/support/guardpress/)
[GuardPress Plugin Page](https://royalplugins.com/guardpress/)
