MCP Errors and Retries
This page explains how AutoCISO MCP reports failures, how the SDKs turn them into typed exceptions, and how retries and rate limits work. For the REST API error format, see Errors and limits.
The error envelope
Most failures are not transport errors. The server returns HTTP 200 with a tool result flagged isError, and the result body is an envelope keyed on error:
{ "error": "SCOPE_FORBIDDEN", "scope": "mcp:read" }
The key is error — there is no message field. Extra fields are code-specific (scope, reason, feature, detail, tool, limit). The SDKs read error, synthesize a human-readable message from the code plus the passthrough fields, and raise a mapped exception. Never write code that reads a message key from this envelope — it doesn’t exist.
Two error planes
AutoCISO MCP failures arrive on one of two planes:
1. Tool-result codes (HTTP 200, inside the {"error": CODE, ...} envelope):
SCOPE_FORBIDDEN, NOT_PERMITTED, INVALID_ARGUMENTS, INTERNAL_ERROR, ORG_SUSPENDED, UNKNOWN_TOOL, RATE_LIMITED, RESULT_TOO_LARGE.
2. HTTP statuses the MCP app itself emits:
401, 404, and 429 — and only those. The MCP app never emits 403 or any 5xx. If you ever see a 403 or a 5xx, it came from the network infrastructure in front of AutoCISO, not the MCP application.
Code → exception → cause → fix
Tool-result codes (HTTP 200)
| Code | SDK exception | Cause | Fix |
|---|---|---|---|
SCOPE_FORBIDDEN |
AutocisoScopeError |
Token lacks the required scope | Recreate the token with the scope the tool needs. See MCP Auth and Scopes |
NOT_PERMITTED |
AutocisoPermissionError; for reason: "FEATURE_NOT_ENTITLED" a ToolError exposing .reason and .feature |
Write tool called but write disabled for org; or reason: "FEATURE_NOT_ENTITLED" with a feature ID — the tool’s feature is no longer in your organisation’s plan (a registration that outlived a downgrade) |
Expected on read-only orgs — not a bug. For FEATURE_NOT_ENTITLED, refresh tools/list; not retried — upgrade the plan in user menu → Billing & Subscription or drop the tool |
INVALID_ARGUMENTS |
AutocisoValidationError |
Bad or missing tool argument | Check the argument against the Tool Catalog |
INTERNAL_ERROR |
AutocisoServerError |
Server-side failure; also returned when a typed tool unexpectedly produces no result at all | Retry; if persistent, contact support |
ORG_SUSPENDED |
AutocisoUnavailableError |
Org is suspended | Resolve the account status |
UNKNOWN_TOOL |
AutocisoUnknownToolError |
Tool name not recognized — including employee_resolve called by a token without pii:read, and any tool whose feature is outside your organisation’s plan; both are unregistered for that token rather than merely hidden |
Check the spelling against the catalog; for employee_resolve, have an Owner create a token with pii:read (see Employee identity); for a plan-gated tool, compare the Requires plan column with your plan |
RATE_LIMITED |
ToolError with code === "RATE_LIMITED" and retryAfterSeconds (retry_after_seconds in Python) |
The token’s per-call window for employee_resolve or iso_audit_simulate is exhausted |
Wait retryAfterSeconds, then retry yourself — the SDK does not retry this code (see below) |
RESULT_TOO_LARGE |
ToolError with code === "RESULT_TOO_LARGE" and limit (bytes) |
The tool’s result exceeded the 256 KiB (262144-byte) cap and was replaced by this error |
Not retried — lower limit, add filters or page with offset; the same call will fail the same way |
HTTP statuses
| Status | SDK exception | Cause | Fix |
|---|---|---|---|
401 |
AutocisoAuthError |
Token missing, malformed, or revoked | Re-copy or recreate the token. See API tokens |
404 |
ServerUnavailableError |
MCP is not enabled for your deployment | Contact support. See Troubleshooting |
429 |
AutocisoRateLimitError |
Rate limit exceeded | Back off and retry (the SDK does this for you) |
Retries
- On by default for reads. Idempotent read tools retry automatically on
429and transient errors, using exponential backoff with jitter, capped at ~3 attempts. - Writes are never auto-retried. None of the five write tools (
risk_create_draft,iso_audit_simulate,supplier_questionnaire_clone,supplier_questionnaire_update_question,supplier_questionnaire_update_option) is retried: the server has no idempotency key, so a blind retry could create a duplicate, andiso_audit_simulatespends an AI request on every call. Handle write failures yourself. FEATURE_NOT_ENTITLEDandRESULT_TOO_LARGEare never retried. Neither changes on its own: the first needs a plan change (then a freshtools/list), the second needs a smaller request.- Tool-plane
RATE_LIMITEDis not automatically retried. It arrives inside the envelope at HTTP 200 with aretryAfterSecondshint, so the HTTP429path never fires and the read-retry loop would burn its attempts in seconds against a window that can be up to an hour long. Both SDKs surface it as a non-retryableToolErrorcarryingretryAfterSeconds; the caller decides when to try again. In TypeScript it is aToolError, not the HTTP-planeRateLimitErrorclass.
employee_resolve and retryAfterSeconds
employee_resolve and iso_audit_simulate are the only tools that return RATE_LIMITED. The employee_resolve window is currently 30 calls per hour per token, counted per server instance, so it is approximate — treat the number as client guidance, never as a property to design against. Batch up to 50 IDs per call and store the mapping on your side; a well-behaved integration rarely meets the limit. Rate-limited calls disclose nothing and are not written to the audit trail.
{ "error": "RATE_LIMITED", "retryAfterSeconds": 1740 }
FEATURE_NOT_ENTITLED and RESULT_TOO_LARGE
{ "error": "NOT_PERMITTED", "reason": "FEATURE_NOT_ENTITLED", "feature": "privacy_operations" }
{ "error": "RESULT_TOO_LARGE", "limit": 262144 }
Both arrive as a ToolError (.reason/.feature on the first, .limit on the second; snake_case in Python) with retryable === false. Before calling a tool, the SDKs’ generated descriptors (TOOL_META[name].requiresFeature in TypeScript, TOOL_META[name].requires_feature or REQUIRES_FEATURE[name] in Python) tell you which feature it needs — compare with the Requires plan column of the Tool Catalog.
Rate limits
Handling errors in code
import {
AutocisoScopeError,
AutocisoPermissionError,
AutocisoRateLimitError,
} from '@autociso/mcp-client';
try {
const risks = await client.risk.registerList();
} catch (err) {
if (err instanceof AutocisoScopeError) {
console.error('Token is missing a required scope');
} else if (err instanceof AutocisoPermissionError) {
console.error('Write not permitted for this org');
} else if (err instanceof AutocisoRateLimitError) {
console.error('Rate limited — the SDK already backed off and gave up');
} else {
throw err;
}
}from autociso_mcp import (
AutocisoScopeError,
AutocisoPermissionError,
AutocisoRateLimitError,
)
try:
risks = client.risk.register_list()
except AutocisoScopeError:
print("Token is missing a required scope")
except AutocisoPermissionError:
print("Write not permitted for this org")
except AutocisoRateLimitError:
print("Rate limited — the SDK already backed off and gave up")Was this page helpful?