# nLight.fit errors and limits

## HTTP status codes

| Status | When | What to do |
| --- | --- | --- |
| `200` | The message was handled. A tool that itself failed still returns 200 with `isError: true` in the result. | Read the result. |
| `202` | The message was a notification (no `id`), so there is no response body. | Nothing. |
| `400` | Malformed JSON, a batch array, an unknown method, bad tool arguments, an unsupported protocol version, or routing headers that disagree with the body. | Fix the request. The JSON-RPC error code says which. |
| `401` | No credential, or one that is unknown, revoked or expired. Response carries `WWW-Authenticate: Bearer`. | Mint a new token. |
| `403` | The token is valid but lacks the required scope, or the request carried a browser `Origin` header. | Use a token with `read` scope; do not call this endpoint from a browser. |
| `405` | The request was not a POST. Response carries `Allow: POST, OPTIONS`. | MCP is POST-only. A `405` to a `GET` is what the Streamable HTTP transport prescribes for a server offering no server-to-client event stream; this one is stateless and has nothing to push. |
| `429` | Rate limit exceeded. Response carries `Retry-After` in seconds. | Back off for the stated interval. |
| `500` | Server-side failure. The message is deliberately generic. | Retry; if it persists it is not something the caller can fix. |
| `503` | The data store could not be reached. | Retry with backoff. |

## JSON-RPC error codes

| Code | Meaning |
| --- | --- |
| `-32700` | Parse error — the body was not valid JSON. |
| `-32600` | Invalid request — not a JSON-RPC message, a batch, or an unsupported protocol version. |
| `-32601` | Method not found. |
| `-32602` | Invalid params — missing `params.name`, or a tool that is not exposed. |
| `-32603` | Internal error. |
| `-32020` | Header mismatch — `Mcp-Method` or `Mcp-Name` disagreed with the body, or was absent when the declared protocol version requires it. |
| `-32001` | Authentication required — no credential was presented, or the one presented was rejected. Travels with HTTP 401 and a `WWW-Authenticate` header naming the authorization server. |
| `-32002` | Forbidden — the credential is valid but lacks the scope the method needs, or the request carried a browser `Origin` and asked for member data. Travels with HTTP 403. |

Handle `-32700` as one of two shapes. A request that declares a content type other than `application/json` reaches the server and is answered with the envelope above. A request that declares `application/json` and is not valid JSON is refused by the platform before this API sees it: **HTTP 400 with an empty body and no `Content-Type`**. Treat an empty `400` as a parse failure.

## Rate limits

The MCP endpoint allows **120 requests per 60 seconds**, counted per user rather than per token. Each JSON-RPC message is one HTTP request, so a multi-step agent run consumes calls quickly; the limit is set to accommodate that while still bounding a runaway loop.

The stated number is the ceiling to design against. It is not a precise meter, so do not build anything that depends on exceeding it.

### Rate limit headers

You do not have to wait for a `429` to learn where you stand. Every response carries `RateLimit-Policy` describing the quota, and every response that spends quota also carries `RateLimit` describing what is left of it. Send a credential and every request is counted, so every response reports both. Send none and `tools/list` is still counted, while the negotiation methods (`initialize`, `server/discover`, `ping`) are answered without touching the limiter and carry the policy alone.

| Header | Example | Meaning |
| --- | --- | --- |
| `RateLimit-Policy` | `"mcp";q=120;w=60` | The fixed shape of the quota: `q` requests per `w` seconds. Stable across responses. |
| `RateLimit` | `"mcp";r=118;t=47` | Live state: `r` requests remaining, `t` seconds until the window refreshes. |
| `RateLimit-Limit` | `120` | The same quota in the superseded three-header form, sent for client compatibility. |
| `RateLimit-Remaining` | `118` | Requests left in this window. |
| `RateLimit-Reset` | `47` | Seconds until the window refreshes. |
| `Retry-After` | `47` | Sent only on a `429`. Seconds to wait before retrying. |

`RateLimit` and `RateLimit-Policy` follow [draft-ietf-httpapi-ratelimit-headers](https://datatracker.ietf.org/doc/draft-ietf-httpapi-ratelimit-headers/). The three-header form is the earlier draft, kept because most existing clients read it. All of them are listed in `Access-Control-Expose-Headers`, so a browser-based client can read them cross-origin.

## Version and lifecycle headers

| Header | Meaning |
| --- | --- |
| `Api-Version` | The API version that served the response. |
| `Link` | `service-desc` points to the OpenAPI document, `service-doc` to the developer documentation, `version-history` to the versioning policy. |
| `Deprecation` | Present only on a deprecated resource. An RFC 9745 structured field date. |
| `Sunset` | Present only when a retirement date is fixed. An RFC 8594 HTTP-date. |

Nothing is deprecated today. See [versioning and deprecation](/docs/versioning) for the notice periods these headers promise.

## Result size

A single tool result is capped at 100,000 characters, roughly 25k tokens. Beyond that the payload is truncated and an explicit `[TRUNCATED: ...]` marker is appended, so a clipped series can never read as a complete one. If you hit it, narrow the date window or name fewer metrics.

## Protocol versions

Send your revision in the `MCP-Protocol-Version` header. Supported, newest first:

- `2026-07-28`
- `2025-11-25`
- `2025-06-18`
- `2025-03-26`

An unrecognised version is refused with a `400` that carries the full supported list in `error.data.supported`, so a client can pick one instead of guessing. Omitting the header entirely is read as `2025-03-26`, the last revision that predates the header.

From `2026-07-28` onward, every request must also carry `Mcp-Method`, and `Mcp-Name` when the message addresses something (`tools/call`). The server checks them against the body and rejects disagreement, so an intermediary cannot route on one operation while the server performs another.

## Scopes

Tokens carry explicit scopes: `read`, `write`. They are not cumulative — a `write` token does not imply `read`. Every MCP method today requires `read`, including `initialize` and `ping`.
