To read every result, follow next_cursor until has_more is false. To handle errors, tell the three kinds apart. To stay inside the limits, send at most 120 reads, 30 writes and 5 deletes a minute for each account. This guide shows each case.
Lists are paged
Tools that return lists (search_jobs, top_matches, list_applications, list_saved_jobs, list_notifications, search_resume) return a page and say whether there is more:
{ "count": 20, "has_more": true, "next_cursor": "anAxOjIw", "matches": [ … ] }
has_more: truemeans the page is not the whole set.- Pass
next_cursorback as thecursorargument, with the same other arguments, to get the next page. - Treat a cursor as opaque text. Do not build one or read one.
limitsets the page size. The default is 20 for most lists, 10 fortop_matchesand 5 forsearch_resume. Lists are capped at 50 rows a page.
Two mistakes to avoid:
- Counting from one page. “How many remote roles pay over 150,000” cannot be answered from one page. Page to the end, or report the number as a lower bound.
- Reading a short page as the end.
search_jobsapplies themin_scorefilter within a page, so a short page withhas_more: truemeans the filter thinned it out, not that the results ran out. Look athas_more, not atcount.
A loop that follows the cursor:
let cursor: string | null = null;
do {
const page = await callTool('search_jobs', { query: 'python', limit: 50, ...(cursor ? { cursor } : {}) });
rows.push(...page.jobs);
cursor = page.has_more ? page.next_cursor : null;
} while (cursor);
An empty result is an answer. If count is 0 and has_more is false, there is nothing to find, and calling again will not change that.
Three kinds of error
| Kind | How it arrives | Example |
|---|---|---|
| The call was refused before it reached MCP | An HTTP status, usually 401 |
A missing, expired or revoked token |
| The message was not valid JSON-RPC | An HTTP 400 or a 200 with an error object and a code |
Bad JSON, a batch, an unknown method |
| A tool ran and failed | A normal 200 with a result whose isError is true |
A missing argument, a missing scope, the rate limit |
Check all three. A fourth kind sits in front of them: a 403 with error="insufficient_scope" and the scope you need, sent when a tool call needs a permission the connection lacks. The last one is the one clients forget: a failed tool call is not a JSON-RPC error, so code that only looks for error will treat a failure as a success.
HTTP 401
The body is {"error":"invalid_token","error_description":"Authentication required"} and the WWW-Authenticate header carries the address of the resource metadata. Refresh the token and retry once. If the refresh also fails, the person has to sign in again. A suspended account gets the same 401 within an hour of the suspension.
JSON-RPC errors
| Code | Meaning | Seen when |
|---|---|---|
-32700 |
Parse error | The body is not JSON. HTTP 400 |
-32602 |
Invalid params | A batch array, or a body that is not an object (HTTP 400), or a tools/call for a tool that is switched off or that this account may never use. That one answers “Tool not available” with the name in data, and does not say which of the two it is |
-32601 |
Method not found | An unknown method. The unknown name is in data |
-32603 |
Internal error | A resource could not be read. The address is in data |
Batch requests (a JSON array of messages) are not supported. Send one message per request.
Messages without an id are notifications. The server answers them with an empty 202 and never with a body.
Tool errors
The result looks like this:
{
"content": [{ "type": "text", "text": "Error executing tool get_job: …" }],
"isError": true
}
The message is written to be read by a person or a model, and it names the fix. The ones you will meet:
- Unknown tool.
Unknown tool: name. The name is wrong, or it is a write tool on a server where writes are off. - Invalid arguments.
Error executing tool get_job: …with a description of the missing or wrong field. The wording comes from the validator and may change, so show it, do not match on it. - Rate limit. See below.
Prompts report problems as JSON-RPC errors with code 0, for example Missing required arguments: {'job_id'}.
The rate limit
Calls are counted per account, across all clients, in three separate buckets a minute: 120 reads, 30 writes and 5 deletes. Every request to the MCP server spends from the read bucket first, so listing tools counts too. A tool that writes or deletes then also spends its own bucket, so a loop of reads cannot use up the writes.
Over the read limit, the request itself is refused with 429, a Retry-After: 60 header and error=rate_limited. Over the write or delete limit, the call returns a tool error that begins Rate limit reached: and says to wait about 60 seconds. similar_jobs also has its own, smaller allowance, because it compares against every open job, and says so the same way. A support agent’s replies and notes also spend the allowance the support app uses.
If the rate limiter itself cannot be reached, the server refuses rather than allow unlimited calls: 503 on a request, or a tool error “The rate limiter is unavailable.” Try again later.
The OAuth endpoints have separate limits per address: 30 a minute on /oauth/authorize, 60 on /oauth/token, 20 on /oauth/revoke and 10 on /oauth/register. Beyond one they answer 429 with Retry-After: 60.
Writes and deletes are written to the request log before they run. If the log cannot be written, the call is refused and nothing changes. Retry later.
To stay under the limit:
- Page with a large
limitrather than many small pages. - Cache what does not change, such as
list_tracksandlist_sources. - On a rate limit answer, wait for the time it gives. Do not retry in a loop: a retry spends calls without changing the result.
What we checked
The error bodies and codes above were produced by calls to a local copy of the server on 2026-10-17. On 2026-10-18 the limits, the 403 step-up and the unavailable-tool answer were updated from the server’s source and not run. The page sizes also come from the source. The rate limit answers were not triggered. None of this was run against the production server.