Limits and errors
Plan-driven limits
Per-account limits are set by your subscription plan. Read them at runtime:
Call
get_accountand tell me my plan limits.
The response includes plan.limits (max sites, max domains, storage, bandwidth) and usage (current counts and bytes). Hitting a plan limit returns a clear error from create_site or a deploy tool - the agent should surface the limit and either free space (delete_site) or suggest an upgrade.
Deploy payload limits
| Constraint | Value | Why |
|---|---|---|
deploy_files total payload | ≤ ~1 MB practical, hard cap ~6 MB | JSON-RPC + Lambda payload caps. For anything larger, use deploy_create_upload. |
deploy_create_upload part size | ~5 MB per part | S3 multipart minimum. The server returns one presigned PUT URL per part. |
| Presigned PUT URL TTL | A few hours (expiresAt returned in the envelope) | Use the URLs promptly; if they expire, call deploy_create_upload again. |
| Recommended bundling | Zip when > 3 files OR any file > ~1 MB | The fileWorker auto-extracts a single-zip upload, preserving subdirectories. |
OAuth TTLs
| Token | Lifetime | Notes |
|---|---|---|
| Access token | 1 hour | Refreshed silently by your MCP client. |
| Refresh token | 90 days | Rotates on each refresh; old refresh token is invalidated. |
After 90 days of inactivity, the next tool call returns 401 and your client opens the consent flow again. See Authentication for the full flow.
Rate limits
The Hostsmith API enforces per-account rate limits to protect partition capacity. The MCP server passes API responses through, so a rate-limit hit surfaces as a tool error with retry guidance. Treat any 429-style error as backoff-and-retry; do not loop without a delay.
Error categories
MCP tools return errors as { isError: true, content: [{ type: "text", text: "<message>" }] }. The text is human-readable and intended for the agent to surface to the user.
| Category | Typical trigger | What the agent should do |
|---|---|---|
| Auth (401) | Access token expired or revoked. | Reconnect the client; the next call re-auths. |
| Forbidden (403) | Token scopes don't cover the call (rare - the server requests the full set). | Reconnect to refresh scopes. |
| Validation (400) | Bad input - subdomain pattern, partition mismatch, missing required field. | Surface the message; do not retry the same call unchanged. |
| Conflict (409) | Subdomain already taken; site name in use. | Pick a different subdomain; for shared domains, omit subdomain to auto-generate. |
| Not found (404) | siteId doesn't exist or is in another partition. | Re-resolve via list_sites; pass partition explicitly if needed. |
| Plan limit | create_site would exceed plan.limits.maxSites. | Surface the limit; delete_site to free space or suggest upgrade. |
| Domain not active | create_site called against a domain still validating or failed. | Surface state; do not retry - the user fixes it in the app. |
| Rate limit (429) | Too many requests in a short window. | Wait and retry once; if it recurs, surface to the user. |
| Server (5xx) | Transient backend issue. | Retry once after a short delay; otherwise surface. |
| Confirmation required | delete_site called without confirm: true. | Confirm with the user, then call again with confirm: true. |
Retry guidance
Safe to retry automatically (with a short delay):
- 5xx server errors.
- 429 rate-limit errors (back off, then retry once).
- Transient network failures during a presigned S3 PUT (the URLs stay valid until
expiresAt).
Do not retry without changes:
- 4xx validation, conflict, or auth errors. Surface the message; the agent or user must change something.
deploy_create_uploadenvelopes afterexpiresAt- calldeploy_create_uploadagain to get fresh URLs.
Diagnosing failed deploys
If deploy_files returns success but get_site shows a non-active status, the deploy was accepted but post-processing (zip extraction, content scan) is still running or failed. Wait a few seconds and call get_site again. If it stays in a failed state, surface the status to the user and inspect the site in the app for details.
