Errors and limits
A failed tool call returns a structured error an agent can act on without guessing, and the limits behind it are the same ones the REST API applies.
The error shape
A failed call is a tool result with isError: true. The same JSON object is in the result's text content and in structuredContent:
{
"code": "AMBIGUOUS_IDENTIFIER",
"message": "More than one account is connected. Choose one of the listed accounts.",
"retry_hint": null,
"user_fixable": true,
"retry_likely_to_succeed": true,
"correlation_id": "mcp_cdd89fd6a3753f77b1402d17",
"next_action": "More than one entry matched. Re-call with one of the ids listed in candidates.",
"candidates": [
{ "id": "acc_01JQZK8N3XV4RTYWB2M6D5F0AC", "name": "Sales account" },
{ "id": "acc_01JQZK9A7M2PQ4RTYWB2M6D5F0", "name": "Recruiting account" }
]
}| Field | Meaning |
|---|---|
code | Stable error code. Branch on this, never on message. |
message | Human-readable description. |
next_action | One sentence saying what to do next: which tool to call, what to pass, or when to retry. |
retry_hint | When and how to retry: null, or an object whose kind is delay (wait delay_ms), backoff (use your own exponential backoff) or never (do not retry). |
user_fixable, retry_likely_to_succeed | Whether a changed call can succeed, and whether the same call is worth retrying. |
correlation_id | mcp_... id to quote when you contact support. |
candidates, unresolved, toolset | Present on specific codes: the choices for AMBIGUOUS_IDENTIFIER, the unresolved filter values for FILTER_CANDIDATES_REQUIRED, the disabled toolset for TOOLSET_DISABLED. |
The codes are the REST API's codes, plus TOOLSET_DISABLED and AMBIGUOUS_IDENTIFIER, which only tools raise. Every code with its cause and fix: Error codes.
Rate limits
MCP and REST share the same rate-limit counters. A tool call counts against the workspace limit and the connected account's per-minute request ceiling exactly as a REST request does, so an agent on MCP and a script on REST draw from one budget. The two surface differently. The workspace limit is checked before the request reaches any tool, so it arrives as an HTTP 429 response to the MCP request itself (code RATE_LIMIT_TENANT, with Retry-After), not as a tool result. The per-account ceiling is checked inside the tool call, so it arrives as a tool error with code RATE_LIMIT_ACCOUNT and retry_hint.delay_ms. Either way, wait that long, then retry. The limits and the backoff algorithm: Rate limits.
Refusals that cost nothing
A call refused because the account has no active seat, or because the tool needs a beta opt-in the workspace has not given, is refused before anything else happens. It takes no LinkedIn action and does not count against the account's request ceiling.
Account safety limits
Each connected account has safety ceilings per action type. get_account reports where the account stands in its quotas[]: each account's actions, ceiling and band per window. By default a breach is reported, not refused, unless update_safety_policy sets posture enforce or LinkedIn paused that action.
- Default posture (
warn): the call goes through and the successful result carries asafety_warning. - Posture
enforce: the call is refused withBUDGET_EXHAUSTED. Nothing reached LinkedIn. It is not a rate limit: do not back off and retry, read the envelope's reset time or change the policy. - LinkedIn paused the action: the call is refused with
PLATFORM_RATE_LIMITnaming the paused row. Wait outretry_afterand use other actions meanwhile.
How ceilings, bands and posture work: Account safety.