Errors
Failures surface at three layers: the HTTP status (did the request reach the server and authenticate?), the JSON-RPC envelope (did the call itself succeed?), and the tool payload (did the tool run and then refuse?). Check all three — the third is the one a shared response handler usually misses.
HTTP status codes
| Status | Meaning |
|---|---|
200 | The request was processed — but the body may still carry a JSON-RPC error (below) |
400 | The body wasn't parseable JSON, wasn't a JSON-RPC envelope at all, or was a batch array over the 64-envelope cap — the error code is -32700 or -32600 |
401 | Missing x-api-key, or the key isn't enabled for MCP — see Authentication & access |
5xx | Transient server-side failure — safe to retry with backoff |
JSON-RPC errors
A failed call returns an error envelope instead of a result:
{
"jsonrpc": "2.0",
"id": 1,
"error": { "code": -32602, "message": "Invalid request parameters" }
}
| Code | Meaning | Usual cause |
|---|---|---|
-32700 | Parse error | The body isn't valid JSON — arrives at HTTP 400 |
-32600 | Invalid request | The envelope wasn't usable. At HTTP 400, the body is valid JSON but not a JSON-RPC object or batch array — or a batch over the 64-envelope cap. At HTTP 200, a tools/call arrived with no usable name — either omitted outright, or because params was sent positionally and a non-object params is read as empty. Not caused by a missing MCP-Protocol-Version header — see Versioning |
-32601 | Method not found | Wrong method name — e.g. tool/call for tools/call |
-32602 | Invalid params | Malformed params: a non-object arguments, or a tool name whose namespace prefix we don't serve (mdmcp-search_brands, not search_brands) — check the Reference. Arguments that merely don't match a tool's input schema do not land here; that call succeeds at HTTP 200 and carries the refusal in its payload — see below |
-32603 | Internal error | Server-side failure while executing — safe to retry |
-32002 | Resource not found | Unknown resource URI — check the Reference for the ones this server publishes |
-32003 | Not authorised | Your key is valid, but your subscription doesn't include that backend — see below |
-32004 | Backend unavailable | That backend is being shed after repeated failures. Back off substantially longer than for -32603 — retrying quickly only extends it |
-32099 | Rate limited | Too many requests — see Rate limits |
Most tool refusals arrive in the payload, not on isError
When a tool runs and then refuses, the call succeeds at the protocol level and
the refusal is in the payload — note isError is false:
{
"isError": false,
"structuredContent": {
"error": {
"code": "invalid_input",
"message": "topic: Input should be 'filter_fields', 'metrics', …",
"field": "topic"
}
}
}
code | Meaning | Retry? |
|---|---|---|
invalid_input | Arguments refused. Often with field naming the one to change — the market-data tools set it consistently, research and messaging only sometimes. | No — fix the request |
not_entitled | The backend refused your subscription's access to this data. | No |
upstream_unavailable | A dependency is failing or slow. | Yes, with backoff |
internal | Our side is wrong: a malformed record, or a guard we can't satisfy. A retry returns the identical error, which is why it isn't worth one. Distinct from -32603 above, which is the gateway failing rather than a tool. | No |
A client branching only on isError reads an in-band refusal as a success, and
then finds no data where it expected some. A client branching only on the payload
crashes on the isError cases, where structuredContent is null. Check
isError first — when it is true the text is prose, not JSON — then check the
payload for error. See
Concepts → Response shape for which refusals use
which channel, and a ready-made unwrapper.
"Not provisioned" has three different shapes
Worth separating, because they arrive at three different layers and only the last
one is a not_entitled payload:
| What is wrong | How it arrives |
|---|---|
| The key is unrecognised, or not enabled for MCP | HTTP 401 — nothing reaches a tool |
| The key is valid, but your subscription does not include that backend | JSON-RPC -32003 at HTTP 200 |
| The key reaches the backend, which refuses the specific data | In-band not_entitled, isError: false |
Empty results aren't errors
A search that returns an empty list succeeded — no match cleared the relevance threshold. Rephrase the query, make it more specific, or batch several phrasings in one call (see Concepts → Tools).
This is distinct from a refusal: an empty answer arrives on the result branch
with no error at all.
Retrying
Every EDITED MCP tool is a read-only search with no side-effects, so retries
are always safe. Retry 5xx, -32603 and -32099 with exponential backoff;
on -32004 back off substantially longer, since that backend is already being
shed and quick retries prolong it. Don't retry 401, -32700, -32600 or
-32602 — those need a fix (key, request shape, or tool-name prefix), not
patience.
An isError: true result is worth exactly one retry. It carries no code
to branch on, and the channel mixes two unlike things: a genuine bug, and a
transient failure raised below the tool's own error handling. Retry once with
backoff; if it comes back identically, treat it as a bug and report it.
Among the in-band codes, only upstream_unavailable is worth retrying.
internal is not — it means a retry returns the same error, not that we are
having a bad minute.
Getting help
If you're stuck — whether a call failed outright or the surface just doesn't look the way you expect — email support@edited.com with:
- the tool name and arguments you called,
- the full error envelope you got back, if there was one — plenty of problems arrive without an error at all,
serverInfo.versionand the surfacefingerprintfrom yourinitializeresponse — between them they identify exactly which build and which tool surface you were talking to, which is usually the difference between a same-day answer and a long conversation (see Versioning),- your session ID — read the
mdmcp+data://session_idresource right after the failing call so EDITED can find your requests in the logs.