examples/MCP-tools Public
id: rate-limits
type: concept
owner: justin
component: rate-limiter
describes:
- src/rate-limiter/**
reviewed: 2026-10-15
Rate limits
Each client gets a token bucket: 30 tool calls to start, one more every two seconds, never more than 30. [[0004-per-client-rate-limit]] has the reasoning.
Where the pieces are
src/rate-limiter/bucket.ts: the arithmetic.take(bucket, now)refills by the time elapsed, spends one token if there is one, and returns the new bucket and a decision:{ ok: true, remaining }or{ ok: false, retryAfterSeconds }. Pure, sotest/rate-limit.test.tsdrives it with a fake clock.src/rate-limiter/index.ts: theRateLimiterDurable Object, bound asRATE_LIMITER. Its one RPC method,take(), loads the bucket from storage, runs the arithmetic, saves the result and returns the decision.src/worker.ts: names the object by the caller'scf-connecting-ipand turns a refusal into the message the model sees. See [[http-transport]].
Behavior worth knowing
- A refused call does not spend anything, so a client that waits the time it was told always gets through.
- A clock that steps backwards adds no tokens.
- After each call the object sets an alarm for the moment its bucket would be full. If nothing else happens first, the alarm deletes the object's storage, and the next call starts from a fresh, full bucket, which is the same state.
- Durable Objects handle one request at a time, so concurrent calls from one client cannot both spend the last token.
Changing the limit
Edit LIMIT in bucket.ts. Buckets already stored keep their token count and refill toward the new capacity. Update the numbers in [[0004-per-client-rate-limit]] only by writing a new decision that supersedes it.