Understand MCP rate limits
MCP and OAuth limits protect the server. They are counted per minute.
Limits by operation
| Bucket | Limit | Applies to |
|---|---|---|
| Protocol and catalog requests | Read rate limit per minute, default 60 | Native protocol requests and standard catalog/readiness requests |
| Task submission | The greater of 120 or 2 × the configured read limit | Tool calls other than pom_get_task, resource reads and prompt requests |
| Task status | The greater of 600 or 10 × the configured read limit | pom_get_task |
| Authentication attempts | The greater of 1,200 or 20 × the configured read limit | MCP requests, per client IP, before credential verification |
| OAuth | 5 per endpoint | Separate registration, token and revocation budgets |
Task submission and status have separate budgets. Polling an accepted task does not consume its submission budget or the catalog budget. Read and write operations share the submission budget because the request only saves work for later execution. Cron does not charge an accepted task a second time.
OAuth registration, token and revocation requests each have their own fixed budget per client IP. Failed attempts also count. Repeated MCP connection attempts have an early per-IP limit before credential verification.
The WordPress 6.8 transport and the bundled WordPress MCP Adapter use the same task submission and status budgets. Concurrent requests cannot share the same remaining slot. A dry_run is also a submitted operation; permission checks do not add another charge.
Configuring the read limit
Settings → POM AI → MCP server → Runtime limits → Read rate limit per minute, default 60.
This setting controls protocol/catalog requests and scales the larger task budgets in the table. Lowering it does not reduce submissions below 120 or task status below 600 per minute. Queue capacity and a single heavy worker still bound execution independently.
How requests are counted
Counting is per authenticated caller, per minute:
- OAuth MCP calls — per client and acting user, shared across that pair’s tokens.
- API key — per key. Everything using that key shares one budget, which is a reason to prefer OAuth when several tools connect.
- Unauthenticated OAuth endpoints — per client IP.
Each minute is its own window. A client throttled at the end of one minute can continue at the start of the next.
One connected client can make several HTTP requests to initialize, discover tools and poll background results. Submission and pom_get_task answer directly without waiting for cron. They can run alongside discovery and other submissions, subject to their own quotas and queue admission. Heavy operations run through WordPress cron, one at a time for the installation.
What being limited looks like
| Where | Response |
|---|---|
| Standard MCP method | Error -32029; inspect data.code, data.status and data.retry_after |
| Native HTTP request limit | HTTP 429 with Retry-After |
| The OAuth endpoints | HTTP 429 with rate_limited and Retry-After |
| Background queue full or busy | pom_ai_task_busy; retry after the returned delay |
| Request limiting is unavailable | pom_ai_mcp_rate_unavailable with status 503; OAuth reports temporarily_unavailable |
A refused submission does not start an operation. A refused status request does not cancel or change work that was already accepted. Tool errors can be returned inside an HTTP 200 response, so inspect the tool result too.
What it protects, and what it does not
It bounds how fast a client can act. That helps against a client stuck in a retry loop, a misconfigured integration polling aggressively, and a runaway session doing more than intended.
Rate limits do not authorize changes. Write gates, the acting user's capabilities, the scopes on the token and approval for the exact changes remain required. See MCP security model.
During setup
Each OAuth endpoint permits five attempts per minute per IP, so a client retrying a failing exchange in a loop can exhaust that endpoint's budget. Registering a client does not consume the token endpoint's budget.
When diagnosing an OAuth failure: change one thing, try once, wait if you see 429. Repeated attempts mask whether your fix worked.
During bulk work
Use bounded operations, submit each intended change once and poll its existing task. The queue may fill before a per-minute budget is exhausted, particularly when cron is delayed or work takes longer to finish.
Continue polling after poll_after_seconds while terminal is false. When it becomes true, inspect the final status and result before continuing. Never automatically replay an uncertain write. See the background result contract.
Reducing how often you hit it
- Ask for specific records rather than broad listings.
- Prefer tools that return counts over tools that return every record.
- Let the client cache the tool list rather than re-listing each turn.
- Send one JSON-RPC message per HTTP request. Both transports reject protocol batches; individual tools may accept their own documented batch inputs.
- Follow
Retry-After, or JSON-RPCdata.retry_after, for a quota refusal andpoll_after_secondsfor a pending task. Polling does not submit the operation again. - Pace large jobs across minute boundaries. Opening another session does not reset the same caller’s budget.
Checking whether limits are the problem
Inspect the response in the client and the recent MCP logs in the settings panel. Repeated authentication denials are sampled, and a request refused before authentication may have no individual log entry. An empty log does not prove the request failed upstream.
If the server reports that request limiting is temporarily unavailable, wait and ask the site administrator to check WordPress storage. The server does not execute the operation while that safeguard is unavailable.
Multisite
Limits are per site, since each site has its own server, tokens and API key. Separate site connections have separate budgets. Requests delegated through the main-site MCP connection also remain subject to that connection's limits.