Class: McpToolkit::RateLimiter
- Inherits:
-
Object
- Object
- McpToolkit::RateLimiter
- Defined in:
- lib/mcp_toolkit/rate_limiter.rb
Overview
A fixed-window request counter backing the authority transport's built-in rate
limiting (McpToolkit::Authority::ControllerMethods#mcp_rate_limit!). It is
storage-agnostic: it counts against the injected cache_store (any
ActiveSupport::Cache::Store — a shared Rails.cache in production, a MemoryStore
in a unit test), so a host enables per-principal throttling by setting
config.rate_limit_max_requests alone, without hand-rolling a limiter.
The window is FIXED, not sliding: every request whose time falls in the same
window-second bucket shares one counter, keyed by that bucket's start
(window_start); the entry expires after window seconds. The counter is
incremented once per call, and the request is allowed while the running count is
<= max_requests, blocked once it exceeds it (so exactly max_requests
requests pass per window).
result = McpToolkit::RateLimiter.new(
key: principal.id, max_requests: 1_000, window: 3_600, cache_store: Rails.cache
).call
result.allowed? # => false once the count exceeds max_requests
result.limit # => 1_000
result.remaining # => max_requests - count, floored at 0
result.reset_at # => epoch seconds of the next window boundary
result.retry_after # => seconds until reset_at (0 when already past)
The cache key is namespaced (mcp_toolkit:rate_limit:<key>:<window_start>) so a
host's own cache entries never collide with the counter.
Defined Under Namespace
Classes: Result
Instance Method Summary collapse
-
#call ⇒ Object
Increments this window's counter and returns the Result.
-
#initialize(key:, max_requests:, cache_store:, window: 3_600, now: Time.now) ⇒ RateLimiter
constructor
A new instance of RateLimiter.
Constructor Details
#initialize(key:, max_requests:, cache_store:, window: 3_600, now: Time.now) ⇒ RateLimiter
Returns a new instance of RateLimiter.
37 38 39 40 41 42 43 |
# File 'lib/mcp_toolkit/rate_limiter.rb', line 37 def initialize(key:, max_requests:, cache_store:, window: 3_600, now: Time.now) @key = key @max_requests = max_requests @cache_store = cache_store @window = window @now = now.to_i end |
Instance Method Details
#call ⇒ Object
Increments this window's counter and returns the Result. Called once per request by the transport's rate-limit hook.
47 48 49 50 51 52 53 54 55 56 57 58 |
# File 'lib/mcp_toolkit/rate_limiter.rb', line 47 def call count = @cache_store.increment(cache_key, 1, expires_in: @window) || 1 allowed = count <= @max_requests Result.new( allowed:, limit: @max_requests, remaining: allowed ? @max_requests - count : 0, reset_at:, retry_after: [reset_at - @now, 0].max ) end |