Reference
Limits & errors.
Everything a client needs to behave well: what will throttle you, how much comes back at once, and what to branch on when a call fails.
Rate limits
Two fixed windows, both per minute. The pre-auth limit is deliberately the looser of the two: grants share egress IPs, so a per-IP cap tighter than the per-grant cap would throttle unrelated callers behind one NAT.
- 120 / minute — per grant
- Applied once your key is resolved. Exceeding it returns
-32000withdata.code: "rate_limited"and aresetAttimestamp. - 300 / minute — per IP, before authentication
- Applied to unauthenticated traffic so an unknown caller cannot force the key-hashing
path repeatedly. Answers HTTP 429 with
Retry-After.
Response budget
A single result is capped at 64,000 bytes
serialized. List tools page within it rather than truncating: when more remains, the result
carries nextCursor — in
_meta and named in the result text,
so it survives a host that drops _meta.
Pass it back unchanged. An absent cursor means you have everything.
Error vocabulary
Branch on error.data.code,
not the numeric JSON-RPC code. The strings are the stable contract; the numeric band can be
renumbered by a protocol revision without breaking you.
- unauthenticated — -32006
- No credential, or one that failed. Not retryable without a different key.
- unauthorized — -32003
- Authenticated, but your identity may not see or modify this row. Row-level security decided it, not the server.
- invalid_input — -32602
- Arguments failed validation. The message names the field. Not retryable unchanged.
- not_found — -32602
- The id does not exist or is invisible to you — deliberately indistinguishable, so the surface cannot be used to enumerate.
- conflict — -32005
- Already exists, or a unique constraint refused it. Often means your write already succeeded.
- rate_limited — -32000
- Back off until
resetAt. Retryable. - upstream_error — -32001
- The database returned something unexpected. Retryable with backoff.
- server_misconfigured — -32000
- Our fault, not yours — a required binding is missing. Not retryable; tell us.
Note that -32000
carries both rate_limited and
server_misconfigured — one is
retryable and one is not. That is precisely why the string, not the number, is the contract.
Writes and retries
Tools whose table has no unique index require an
idempotency_key: a retry with the
same key returns the stored result instead of acting twice. This matters most where a write
also notifies a real person — see the badges in the
catalogue.