MCP endpoint and headers
The address, authentication headers, protocol versions, sessions, rate limits and error shapes of the PowerWP AI MCP endpoint, for developers.
PowerWP AI exposes one MCP server on your site. Most people never touch it, because AI apps set themselves up from the Connections screen. This page is for developers who build or debug a client.
The endpoint#
https://your-store.com/wp-json/wpai/v1/mcpWith plain permalinks the address is https://your-store.com/?rest_route=/wpai/v1/mcp. Your exact address is shown under PowerWP AI › Help › Connection details.
- Transport: Streamable HTTP. Send JSON-RPC messages with
POST. Every answer is a single JSON object. There are no event streams, because long jobs such as backups run as jobs that the client polls. GETanswers 405.DELETEwith anMcp-Session-Idheader ends that session.- Server: name
wpai-mcp, titlePowerWP AI, version of the plugin. It offers tools and prompts, and no resources. - Requests must use HTTPS. Local development sites are exempt, and Settings › Connection security › Trust X-Forwarded-Proto helps behind a proxy.
- A request with an
Originheader must come from your site's own address. Clients that are not browsers send none.
Authentication#
| Header | Use |
|---|---|
X-WPAI-Token: <key> | Recommended. It works on hosts that strip the Authorization header. |
Authorization: Bearer <key> | Also accepted. |
Connection keys begin with wpai_. Apps that connect by signing in receive access tokens that begin with wpaio_. Management keys cannot be used on this endpoint: they get a 403 wrong_credential_type error. Keys are shown once when you create a connection.
Sign-in (OAuth)#
New in 1.6. Apps that support the MCP authorization specification can connect with just the endpoint address. A request without credentials gets a 401 with this header, which points the app to the sign-in:
WWW-Authenticate: Bearer realm="wpai-mcp", resource_metadata="https://your-store.com/wp-json/wpai/v1/oauth/protected-resource"The site is its own authorization server. The app registers (POST /wp-json/wpai/v1/oauth/register), sends you to the approval screen in wp-admin, where you choose what it may do, and then exchanges the code at /wp-json/wpai/v1/oauth/token (PKCE S256 required). Access tokens last 1 hour, refresh tokens 60 days, and /wp-json/wpai/v1/oauth/revoke ends them. The approval creates a normal connection, with the same permissions, plan limits, change log and activity log.
Sign-in needs pretty permalinks, HTTPS on public sites, and the setting Sign-in for AI apps (on by default). See Sign-in for AI apps.
Protocol versions#
| Version | Style |
|---|---|
2026-07-28 | Stateless. No initialize and no session. |
2025-11-25, 2025-06-18, 2025-03-26, 2024-11-05 | Sessions, with an initialize handshake. |
2026-07-28. Every request carries params._meta with io.modelcontextprotocol/protocolVersion and io.modelcontextprotocol/clientCapabilities, and optionally io.modelcontextprotocol/clientInfo. The headers MCP-Protocol-Version, Mcp-Method and, for tools/call and prompts/get, Mcp-Name must match the body. With Strict MCP headers on (the default), a missing header is an error too. Results carry resultType: "complete", and list results add ttlMs (300000) and cacheScope.
Older versions. initialize answers with an Mcp-Session-Id header. Send it back on later requests. A request without it still works, and the protocol is then taken from the MCP-Protocol-Version header, or assumed to be 2025-03-26. An unknown session gets a 404 and code -32001: initialize again. Idle session records are removed after 7 days. JSON-RPC batches of up to 50 messages are accepted for compatibility.
Methods: server/discover, initialize (older versions), ping and logging/setLevel (older versions only), tools/list, tools/call, prompts/list and prompts/get. Notifications get a 202 without a body.
Example for the stateless style:
curl -X POST "https://your-store.com/wp-json/wpai/v1/mcp" \
-H "Content-Type: application/json" \
-H "X-WPAI-Token: $WPAI_TOKEN" \
-H "MCP-Protocol-Version: 2026-07-28" \
-H "Mcp-Method: tools/call" \
-H "Mcp-Name: site_overview" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"site_overview","arguments":{},"_meta":{"io.modelcontextprotocol/protocolVersion":"2026-07-28","io.modelcontextprotocol/clientCapabilities":{}}}}'Tools and prompts#
tools/list returns only the tools the connection's permissions allow. With read-only mode on, or on the Free plan, the write tools are not listed. There are 85 tools in 14 categories: see Tools. Each tool carries the annotations readOnlyHint, destructiveHint, idempotentHint and openWorldHint.
Results are text. A result is cut at 100,000 characters unless the tool allows more, with a note that says how to narrow the request. When a call records changes, the result ends with a line such as "Change journal: #12, #13 recorded (undo with changes_rollback)."
The recipes are the server's prompts. prompts/list returns them and prompts/get returns one with its arguments filled in. Claude Code shows them as slash commands, and apps without prompts use the tools recipes_list and recipe_get. See Recipes.
Rate limits and lockouts#
- Rate limit per key: 240 requests a minute by default (10 to 5,000). Above it you get HTTP 429 and a
Retry-Afterheader. - Lockout: after 10 failed authentications from one IP address (3 to 1,000), that address is locked out for 15 minutes (1 to 1,440). A valid key is never counted as a failed attempt.
Both are set under Settings › Connection security.
Errors#
Authentication problems return an HTTP error with a JSON-RPC body. code is the HTTP status and data.reason says what happened:
{"jsonrpc":"2.0","error":{"code":401,"message":"Authentication required. Send your PowerWP AI token in the \"X-WPAI-Token\" header (or \"Authorization: Bearer <token>\").","data":{"reason":"missing_credentials"}}}| Status | reason | Meaning |
|---|---|---|
| 401 | missing_credentials, invalid_token, token_revoked, token_expired | No key, a wrong key, or a connection that was revoked or has expired. |
| 403 | https_required, ip_not_allowed, wrong_credential_type, user_not_allowed | HTTP instead of HTTPS, an IP address outside the allow-list, a management key, or a connection whose WordPress user is no longer an administrator. |
| 429 | locked_out, rate_limited | Too many failed attempts, or too many requests. See Retry-After. |
| 503 | mcp_disabled | The AI access switch is off. |
Protocol problems use the JSON-RPC codes:
| Code | Meaning |
|---|---|
| -32700 | The body is not valid JSON. |
| -32600 | Not a valid request, a wrong method for the endpoint, or an unsupported protocol version on older sessions. |
| -32601 | Unknown method. |
| -32602 | Invalid parameters, such as an unknown tool, prompt or cursor. |
| -32603 | Internal error. |
| -32001 | Session not found. |
| -32020 | An MCP header is missing or does not match the body. |
| -32022 | Unsupported protocol version. data.supported lists the versions. |
A tool that fails returns a normal result with isError: true. The text starts with Error:, or with Invalid arguments: and a list. A missing permission answers "Permission denied", followed by {"required_scope": "files"} with the internal permission name from Permissions. On the Free plan the data also carries "license": "read_only".
If PHP stops with a fatal error during a tool call, the result is an error that names the tool, the error and the change IDs recorded before the crash.