MCP rate limiting

POM AI counts requests in per-minute buckets associated with the authenticated client or, when necessary, a hashed IP-based key.

Limits

  • Protocol and catalog requests use the administrator-configured MCP rate, defaulting to 60 per minute. Native mode counts authenticated protocol requests; standard mode applies it to catalog listings and readiness requests.
  • Task submissions use a separate task-submit bucket: max(120, configured_rate × 2) per minute. It covers tool calls other than pom_get_task, resource reads and prompt requests.
  • pom_get_task uses a separate task-status bucket: max(600, configured_rate × 10) per minute.
  • MCP authentication attempts have an early per-IP budget of max(1200, configured_rate × 20) per minute.
  • OAuth registration, token and revocation requests each use a separate fixed limit of 5 per minute per IP.

The setting is clamped to at least one. Counters are aligned to the current UTC minute and retained slightly longer than a minute to cover the bucket boundary.

Both transports use the same submission and status budgets. A write submission, including a preview, consumes one submission slot. It does not also consume a separate write quota or the catalog quota. An accepted task is not charged again when cron executes it, including when execution is delegated to another site. Permission checks do not add charges.

Standard MCP rate failures retain JSON-RPC -32029 and include data.code and data.status. Quota failures also include retry_after, bucket, operation and limit in data. A limiter failure is distinguishable by data.code: pom_ai_mcp_rate_unavailable and data.status: 503; it does not claim the quota was exhausted. Failures before JSON-RPC dispatch use the corresponding HTTP status.

Native request-limit failures use HTTP 429 with Retry-After. OAuth quota failures use HTTP 429 with rate_limited and Retry-After. The delay is the remaining time until the next minute window, with a minimum of one second. A limiter failure uses HTTP 503; OAuth reports temporarily_unavailable. See the error contract.

Concurrent work and request size

Submitting a task and reading it with pom_get_task are synchronous HTTP operations. They return without waiting for cron or a heavy worker. Public MCP requests do not reserve an installation-wide request slot; queue admission itself remains atomic and bounded. Authentication, quotas and input validation apply to every request.

Heavy MCP operations share one background execution slot for the whole installation. Administrator AI tools run directly in their browser requests and do not use this slot. Work waits in WordPress cron rather than keeping the originating HTTP request open. Concurrent discovery or status polling does not increase heavy-operation concurrency or queue capacity.

The queue accepts at most 64 queued or running tasks and retains at most 512 task receipts. When admission is full or busy, pom_ai_task_busy refuses the new task without starting its operation. Task inputs are limited to 1 MiB and retained results to 2 MiB. A shared 128 MiB allowance covers task payloads and results, reserving 2 MiB for each accepted operation until it finishes.

When receipt or byte capacity is needed, admission removes the oldest terminal receipts first, without removing queued or running tasks or their separate write approvals. If capacity still cannot be reserved, it refuses admission. Queued tasks have one hour to start; results expire up to one hour after completion and may be removed earlier under capacity pressure. Cron removes expired records. Save results when received; a missing receipt never proves that a write did not run. Use smaller paginated operations and backoff; these limits are separate from the per-minute quota.

Each destination site can have one next operation ready for its cron runner. A delayed site's cron therefore does not prevent another site's ready operation from competing for the single shared heavy worker. Later operations for the same site stay queued. A periodic recovery event restores missing scheduled work without replaying an operation that already started. New submissions do not postpone recovery that is already due. If another POM AI batch holds the execution slot, the ready worker retries 30–60 seconds later without waiting in PHP.

These limits cover MCP task storage and MCP heavy-operation concurrency. They do not set a CPU percentage, PHP memory ceiling or total PHP process count: WordPress startup, incoming requests, cron runners and other plugins still use server resources. The 128 MiB allowance excludes database overhead, other plugin data and ordinary temporary uploads.

Send one JSON-RPC message per HTTP request, no larger than 1 MiB. Domain tools retain their documented batch inputs. Long term batches check a 15-second work budget between entries; inspect partial results before continuing. This budget does not interrupt an already running WordPress callback.

Human review does not reserve a worker. Poll the preparation task to obtain the pending review; after confirmation, inspect it with pom_site_get_write_job before executing its operations.

Client strategy

Use pagination responsibly, cache discovery results for the current connection, and avoid reading the same resource before every tool call. Serialize consequential writes rather than firing a broad batch of independent calls. On rate limit, wait for the next minute boundary and retry with backoff; immediate loops only extend the failure pattern.

Follow poll_after_seconds on each task receipt while terminal is false. It starts at five seconds and increases to thirty seconds for older tasks, reducing repeated status requests while the queue is busy. Status requests count only toward their separate task-status budget, in addition to the early authentication-attempt check. A catalog or submission quota does not consume that status budget.

Use phase to distinguish waiting_for_dispatch, waiting_for_cron and executing; created_at and started_at describe the task's timing. Terminal phases match their final status. Stop automatic polling when terminal becomes true and inspect the final result, including nested network errors. See the full task receipt contract.

Do not automatically retry non-idempotent writes after a lost response. Read the target state first.

The rate limit protects the WordPress site; it is unrelated to POM AI credit balances and does not change provider quotas.

Related guides: rate-limit troubleshooting, transport, and read-only audit recipe.