Skip to content
reaatechREAATECH

@reaatech/mcp-gateway-rate-limit

npm v1.0.0

Enforces per-tenant rate limits using a token bucket algorithm with support for in-memory or Redis-backed storage. It provides an Express middleware function and a set of utility classes for tracking request quotas and managing standard rate-limit HTTP headers.

@reaatech/mcp-gateway-rate-limit

npm version License: MIT CI

Status: Pre-1.0 — APIs may change in minor versions. Pin to a specific version in production.

Per-tenant rate limiting with token bucket algorithm. Supports in-memory and Redis-backed storage with atomic Lua scripts, daily quota tracking, and standard X-RateLimit-* response headers.

Installation

terminal
npm install @reaatech/mcp-gateway-rate-limit
# or
pnpm add @reaatech/mcp-gateway-rate-limit

For Redis support:

terminal
npm install redis

Feature Overview

  • Token bucket algorithm — per-key refill with configurable capacity and burst
  • Two storage backends — in-memory (Map-based) and Redis (atomic Lua scripts)
  • Daily quota tracking — separate limits for per-minute and per-day windows
  • Standard HTTP headersX-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, Retry-After
  • JSON-RPC error format — 429 responses follow MCP conventions
  • Express middleware — drop-in with createRateLimitMiddleware()
  • Dual ESM/CJS output — works with import and require

Quick Start

typescript
import {
  createRateLimiter,
  createRateLimitMiddleware,
} from "@reaatech/mcp-gateway-rate-limit";
import express from "express";
 
// In-memory rate limiter (development / single-process)
const limiter = createRateLimiter("memory", {
  requestsPerMinute: 100,
  requestsPerDay: 10000,
  burstSize: 50,
});
 
const app = express();
app.use(createRateLimitMiddleware(limiter));
typescript
// Redis-backed rate limiter (production / multi-process)
import { createClient } from "redis";
import { createRateLimiter } from "@reaatech/mcp-gateway-rate-limit";
 
const redis = createClient({ url: "redis://localhost:6379" });
await redis.connect();
 
const limiter = createRateLimiter("redis", {
  requestsPerMinute: 1000,
  requestsPerDay: 100000,
  burstSize: 50,
}, redis);

API Reference

RateLimiter (class)

MethodDescription
checkLimit(key, config?)Check if request is allowed for a key. Returns RateLimitResult.
getRemaining(key)Get remaining tokens for a key
reset(key)Reset limit state for a key
close()Clean up backend connection

createRateLimiter(storeType, defaultConfig?, redisClient?)

Factory function. storeType is 'redis' or 'memory'.

Token Bucket

ExportDescription
createTokenBucketConfig(rpm, burstSize?)Create bucket config from requests-per-minute
consumeTokens(state, config, tokens?)Try to consume tokens, returns { allowed, newState }
timeUntilAvailable(state, config, tokens?)Calculate milliseconds until tokens available
createBucketState()Create initial bucket state
TokenBucketState{ tokens: number, lastRefill: number }
TokenBucketConfig{ capacity: number, refillRate: number }

Quota Manager

ExportDescription
QuotaManagerDaily quota tracker: checkQuota, getRemaining, reset, getUsage, cleanup, clear

Middleware

ExportDescription
createRateLimitMiddleware(limiter)Express middleware — checks limit, sets headers, returns 429 on exceeded
rateLimitErrorResponse(res, result)Send 429 JSON-RPC error response
addRateLimitHeaders(res, result)Add X-RateLimit-* headers to any response

Types

TypeDescription
RateLimitResult{ allowed, remaining, limit, reset, retryAfter }
RateLimitConfig{ requestsPerMinute, requestsPerDay, burstSize }
RateLimitStoreInterface: checkLimit, getRemaining, reset, close
QuotaResult{ allowed, remaining, limit, reset }

Rate Limit Response

When rate limited, the middleware returns:

json
{
  "jsonrpc": "2.0",
  "id": "1",
  "error": {
    "code": -32000,
    "message": "Rate limit exceeded",
    "data": {
      "retryAfter": 60,
      "limit": 1000,
      "remaining": 0,
      "reset": 1713225600
    }
  }
}

Usage Patterns

Per-tenant rate limiting

typescript
import { createRateLimiter } from "@reaatech/mcp-gateway-rate-limit";
 
const limiter = createRateLimiter("redis", defaultConfig, redis);
 
async function handleRequest(req: Request) {
  const tenantId = req.authContext?.tenantId ?? "unknown";
  const tenantConfig = getTenant(tenantId);
 
  const result = await limiter.checkLimit(
    `tenant:${tenantId}:rpm`,
    tenantConfig?.rateLimits,
  );
 
  if (!result.allowed) {
    throw new Error(`Rate limited — retry after ${result.retryAfter}s`);
  }
}

License

MIT