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-RPC data.retry_after, for a quota refusal and poll_after_seconds for 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.