---
title: Troubleshooting
description: Fix the most common MCP server failures, organized by symptom.
url: https://developers.wpengine.com/docs/ai/api/mcp/troubleshooting/
image: https://developers.wpengine.com/og/docs/ai/api/mcp/troubleshooting.png
---

# Troubleshooting

This page lists fixes for common Model Context Protocol (MCP) server failures, organized by symptom. You can also search for a specific error code: each code appears in the section that resolves it.

**Before you work through this page, read the `help` and `next_steps` on the error itself.** The server generates them from live state. They name your actual site and the actual missing argument, so they’re more specific than anything on this page. This page covers the failures where that isn’t enough, or where the fix isn’t where you’d expect.

The server returns a tool failure as a normal response that carries an `error.code`, not as an HTTP error. At the transport layer, a failing call still looks like a success. Your client should show you the code.

## Fix a client that shows no tools

If your client shows no tools at all, the connection is failing before tool discovery.

1. Confirm the endpoint is exactly `https://api.ai.wpengine.com/v1/mcp`. The transport is Streamable HTTP, and the client sends requests as `POST`.
2. Confirm the client authenticated with OAuth. In Claude Code, run `/mcp`, select the server, and select **Authenticate**. In Claude Desktop, select **Connect** on the connector. See [Authentication](https://developers.wpengine.com/docs/ai/api/authentication/#authenticate-mcp-clients-with-oauth).
3. In Claude Code, run `claude mcp list` to confirm the server is registered. A server added without `--scope user` appears only in the directory you added it from.
4. Reproduce the problem outside your client with [MCP Inspector](https://github.com/modelcontextprotocol/inspector). If MCP Inspector lists tools and your client doesn’t, the problem is client configuration, not the server.

Unlike a tool error, an authentication failure at this stage is a real HTTP 401. If sign-in succeeds and every request then fails with HTTP 403 `entitlement_required`, see [Fix `entitlement_required` by turning on AI](https://developers.wpengine.com/docs/ai/api/mcp/troubleshooting/#fix-entitlement_required-by-turning-on-ai).

## Fix `forbidden` on every tool call

Every tool call fails with `forbidden`, and the message says the credential is not accepted for the tool.

This happens when you configure the client with an **API key**. The MCP server no longer accepts API keys. A key still connects, but every tool call fails. A tool call must come from a user authenticated with OAuth.

**Fix:**

1. Remove the key or token and the `Authorization` header from the client configuration.
2. Authenticate with OAuth. See [Connect Claude Code](https://developers.wpengine.com/docs/ai/api/mcp/how-to/configure-claude-code/) or [Connect Claude Desktop](https://developers.wpengine.com/docs/ai/api/mcp/how-to/configure-claude-desktop/).
3. Retry the request.

## Fix a token rejected with HTTP 401 or 403

The request fails before any tool runs. This is an HTTP error, not the tool error `forbidden`.

If you signed in with OAuth but didn’t grant a permission that a tool needs, the server returns HTTP 403 with `WWW-Authenticate: Bearer error="insufficient_scope"`. The header names the scopes to request. A client that supports step-up authorization asks you to sign in again. Otherwise, sign in again and grant the permissions the tool needs.

The server returns HTTP 401 for a site service token that doesn’t verify. It returns HTTP 403 with `insufficient scope` for one that verifies without the accounts scope. Don’t use a site service token in an MCP client. Authenticate with OAuth instead. See [Connect Claude Code](https://developers.wpengine.com/docs/ai/api/mcp/how-to/configure-claude-code/) or [Connect Claude Desktop](https://developers.wpengine.com/docs/ai/api/mcp/how-to/configure-claude-desktop/).

## Fix `entitlement_required` by turning on AI

The server returns `entitlement_required` when AI isn’t turned on for your account. The error arrives in one of two ways:

- **Every request fails with HTTP 403** before any tool runs. The message is `This account is not entitled to use WP Engine AI APIs.` None of the WP Engine accounts you belong to has AI turned on. Your client might show this as a server that fails to connect.
- **One tool call fails** with the same message, or with `AI is not turned on for the WP Engine account this call acts on`. The call names an account that has AI turned off. `list_accounts` leaves those accounts out, so pass an `account_id` that it returns.

**Fix:** An account owner accepts the WP Engine AI terms and turns on AI for the account in the WP Engine User Portal. Then retry the request.

## Fix `not_personally_connected` by connecting your WordPress user

A tool returns `not_personally_connected` when your WordPress user isn’t connected to the site.

Registering a site to your account isn’t the same as connecting yourself to it. The second connection is per person and requires explicit consent in WordPress admin. See [The two connections](https://developers.wpengine.com/docs/ai/api/site-and-account-connections/).

If the error names one site, it includes `connect_url` for that site:

1. Log in to that site’s WordPress admin **first**, then open `connect_url`. If you open it while logged out, you land on a login screen and lose the flow.
2. On the consent screen, confirm with **Connect WordPress user**.
3. Retry the original request.

If the request didn’t name a site, the error has no `connect_url`. Choose the site to connect yourself. Your client shouldn’t choose one for you.

1. Call `list_account_sites` with `limit` and `offset`, starting at `offset` 0. Increase `offset` by `limit` until you have covered `pagination.total` from the first page. A page past the end reports `total` 0, so keep the first page’s total.
2. Each site includes `requires_https`, taken from its `site_url` on that same response. Use each site’s `display_name` and `site_url` to choose a site where `requires_https` is false. If every site is true, enable Secure Sockets Layer (SSL) on a site first. Don’t open a connect link for a site where `requires_https` is true.
3. Call `list_site_abilities` with the chosen `site_id`. That response carries the Connect link when the site can be connected.
4. Log in to that site’s WordPress admin, open the link, and confirm with **Connect WordPress user**.
5. Retry the original request.

The link is the only entry point to this flow. WordPress has no button to start it manually.

A `search_abilities` call from a signed-in user with no connected sites doesn’t return this error. Instead, it returns `found: false`, `reason: no_connected_sites`, and the same `help` and `next_steps`.

## Fix `needs_reconnect` on a stale connection

If a connection that worked before now reports that it’s stale, the error is `needs_reconnect`. The connection existed and has lapsed.

Follow the three steps for an error that names one site, in [Fix `not_personally_connected` by connecting your WordPress user](https://developers.wpengine.com/docs/ai/api/mcp/troubleshooting/#fix-not_personally_connected-by-connecting-your-wordpress-user). The error carries the link. Nothing is wrong with the site registration or your sign-in.

If the error is `site_requires_https` instead, the site isn’t served over HTTPS, and you can’t establish a WordPress user connection at all. Enable SSL first. The server also returns this code on the first attempt to connect an HTTP site, before any connection exists. `list_account_sites` sets `requires_https: true` on those sites.

## Find a site missing from `list_account_sites`

Check these causes in order:

1. **Check the status filter.** Unless you pass `status`, the tool returns only active registrations. To see the rest, ask for `revoked` or `suspended`.
2. **Check the account.** `site_not_found` deliberately doesn’t distinguish “no such site” from “belongs to someone else.”

If you name the site directly, the two codes tell you different things:

- `site_not_found` means nothing matched the identifier you gave, or the site belongs to another account. The server intentionally doesn’t distinguish the two.
- `site_not_active` means the server *found* the site, but its registration is revoked or suspended. The identifier was right, and the site was withdrawn.

## Find an ability missing from search results

If search doesn’t return an ability that the site has, work through these checks:

1. **Check that you’re connected to the site.** Ability listing and search cover only sites where your WordPress account connection is active.
2. **Check whether the catalog is out of date.** Search reads a cached catalog and never syncs it. Under `degraded_sites`, `search_abilities` lists every site whose catalog might lag the site. That includes a site that hasn’t synced recently, has never synced, or failed its last sync (`abilities_sync_status` of `error`). Call `list_site_abilities` for that site, which refreshes an out-of-date catalog, and then search again.
3. **Check whether the ability is withheld.** Some abilities are blocked from remote execution and return `ability_unavailable`. Retrying doesn’t help; the change has to happen in WordPress admin.

A search with no matches isn’t an error. It returns `found: false` with a `reason`:

- `no_semantic_match` is the only reason that improves on a rephrase.
- `no_connected_sites` means [a missing WordPress account connection](https://developers.wpengine.com/docs/ai/api/mcp/troubleshooting/#fix-not_personally_connected-by-connecting-your-wordpress-user). Retrying returns the same answer.
- `empty_catalogs` means nothing has synced yet. Retrying returns the same answer.

Search also drops weak matches instead of padding the result, so a plausible query can return nothing. Search finds abilities; it doesn’t browse them. To see everything a site can do, use `list_site_abilities`.

## Fix knowledge base search failures

The two “not found” codes mean different things:

- **`knowledge_base_not_found`** from `get_site_knowledge_base` — the *site* has no usable knowledge base. The error carries a `setup_url` and a three-step repair walkthrough. Follow the walkthrough, then call `get_site_knowledge_base` again to read the new `collection_id`.
- **`collection_not_found`** from search or fetch — the `collection_id` *you passed* is wrong or stale. Read it again from `get_site_knowledge_base`; don’t reuse a cached one.

If `search_knowledge_base` reports that `collection_id` is required, call `get_site_knowledge_base` first. You can’t construct the collection ID from the site ID.

Fetching a document by an unknown ID isn’t an error. The call succeeds and returns `not_found: true`.

## Fix `rate_limited` errors

The server returns `rate_limited` when your client makes too many calls in a short window. The server applies two separate budgets, and the message says which one you hit:

- **`read tool rate limit exceeded`** — 120 calls per minute across `list_accounts`, `list_account_sites`, `search_abilities`, `list_site_abilities`, the knowledge base tools, and the `wpe_*` hosting tools. A hosting tool reports it as `too many hosting tool calls; retry after a short delay`.
- **`ability execution rate limit exceeded`** — `run_site_ability` only: 30 calls per minute on one site, or 60 across all your sites.

These limits are provisional, and WP Engine may change them.

Wait a minute, then ask again. Hitting one budget doesn’t affect the other, so a blocked search doesn’t stop you from running an ability.

If you reach the read limit repeatedly, your client is usually repeating calls, such as searching every site or reading a knowledge base again. Narrow the request to a single site or a more specific question.

`service_unavailable` isn’t a rate limit. It indicates a temporary server-side issue. Retry the request shortly.

## Verify a write after `ambiguous_outcome`

`ambiguous_outcome` means a mutating ability failed in a way that leaves it unknown whether your site applied the change. The write might or might not have happened.

> **Verify before you retry**
>
> Check the site before you retry. For example, confirm whether the ability created the post or activated the plugin. Repeating the request can duplicate a write that already succeeded.

`ambiguous_outcome` is the only error code with that property. The server raises `timeout` and `unavailable` only for reads. They’re explicitly safe to retry, which is why their `help` text says so.

## Look up other error codes

The message that comes with each of these codes usually explains it:

| Code                         | What it means                                                                                                                                                                                      |
| ---------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `account_not_found`          | The `account_id` isn’t one you belong to, or doesn’t exist. Call `list_accounts` and pass one of its values.                                                                                       |
| `insufficient_scope`         | Your sign-in didn’t grant the permission this tool needs. The message names it. Sign in again and grant it.                                                                                        |
| `insufficient_credits`       | The account’s credit balance is exhausted. Add credits in the User Portal; `portal_url` links there when set.                                                                                      |
| `billing_account_pending`    | AI billing isn’t set up for the account yet. The account owner sets it up in the User Portal. If they set it up recently, try again shortly.                                                       |
| `credits_unavailable`        | The credit balance check is temporarily unavailable. Try again shortly.                                                                                                                            |
| `invalid_request`            | An argument failed validation, and the message names the problem. The server rejects unknown fields instead of ignoring them. If an ability needs one of several argument sets, `help` lists them. |
| `ability_not_found`          | The name isn’t in that site’s catalog, or isn’t runnable. Check the spelling, or search again.                                                                                                     |
| `run_failed`                 | Your site rejected the call. The server appends the WordPress error to the message. Fix that error, then retry.                                                                                    |
| `knowledge_base_unavailable` | Search is temporarily unavailable. Try again shortly.                                                                                                                                              |
| `request_too_large`          | The input exceeds the size cap. Send less.                                                                                                                                                         |
| `run_url_untrusted`          | The site advertises a location that’s inconsistent with its registration. Contact support.                                                                                                         |
| `internal_error`             | An unexpected server error. Retry once, then contact support.                                                                                                                                      |

## Contact support when nothing here matches

Collect the `request_id` from the error and include it in your support request. WP Engine support uses it to locate the call. `internal_error` always includes a `request_id` and never includes a suggested fix.

---

[Site and WordPress account connections](https://developers.wpengine.com/docs/ai/api/site-and-account-connections/)Why most of the failures on this page exist, and which tools need which connection.

[Authentication](https://developers.wpengine.com/docs/ai/api/authentication/#choose-a-key-type)User versus account keys, and what a 401 and a 403 each tell you.

## Was this helpful?

Thanks for the feedback!

```json
{"@context":"https://schema.org","@graph":[{"@type":"WebSite","@id":"https://developers.wpengine.com/#website","name":"WP Engine Product Documentation","url":"https://developers.wpengine.com/","description":"Resources for developing, deploying, and scaling on WP Engine.","inLanguage":"en-US","keywords":["API documentation","product documentation","headless WordPress","managed WordPress hosting"],"publisher":{"@id":"https://wpengine.com/#organization"}}]}
```
