Getting it into your agent
One page per mod, every tool's command on it. A separate URL per tool would split the same page into five that compete with each other.
npx agentmods add rules/pauljphilp/effectpatterns/add-rate-limiting-to-apisgit clone --depth 1 https://github.com/PaulJPhilp/EffectPatternsWhat it costs to keep this loaded
Counted locally with the o200k_base tokenizer, which is exact for GPT models; Claude uses its own tokenizer and its counts differ. Treat this as one consistent yardstick across the catalogue rather than a bill. Prices are per million input tokens.
| Model | Per session | Once invoked |
|---|---|---|
| Fable 5 | $0.01218 | $0.01218 |
| Opus 5 | $0.00609 | $0.00609 |
| Sonnet 5 | $0.00244 | $0.00244 |
| Haiku 4.5 | $0.00122 | $0.00122 |
Grade A, and why
add-rate-limiting-to-apis scanned grade A with 0 findings against 26 rules in 11 categories — prompt injection, anti-refusal, data exfiltration, privilege escalation, supply chain, agent snooping, system-prompt leakage, SSRF and excessive agency — measured yesterday.
A static scan of the body, not an audit. Every finding is printed with the line that produced it so you can judge whether it matters here. A mod is markdown that instructs an agent; that is exactly why what it instructs is worth reading.
Nothing flagged
None of the 26 patterns this scan looks for appear in this file: no shell pipes, no recursive deletes, no credential paths, no hidden text, no instruction-override or anti-refusal phrasing, no agent-config snooping. That is not a guarantee, it is the absence of the things that are checkable.
How it starts
The opening of the file, as written. The whole thing — 198 lines — stays where its author put it; the contents beside it link to each section on GitHub.
description: Use a rate limiter service to enforce request quotas per client. globs: "**/*.ts" alwaysApply: true
Add Rate Limiting to APIs
Rule: Use a rate limiter service to enforce request quotas per client.
Example
import { Effect, Context, Layer, Ref, HashMap, Data, Duration } from "effect"
import { HttpServerRequest, HttpServerResponse } from "@effect/platform"
// ============================================
// 1. Define rate limit types
// ============================================
interface RateLimitConfig {
readonly maxRequests: number
readonly windowMs: number
}
interface RateLimitState {
readonly count: number
readonly resetAt: number
}
class RateLimitExceededError extends Data.TaggedError("RateLimitExceededError")<{
readonly retryAfter: number
readonly limit: number
}> {}
// ============================================
// 2. Rate limiter service
// ============================================
interface RateLimiter {
readonly check: (key: string) => Effect.Effect<void, RateLimitExceededError>
readonly getStatus: (key: string) => Effect.Effect<{
remaining: number
resetAt: number
}>
}
class RateLimiterService extends Context.Tag("RateLimiter")<
RateLimiterService,
RateLimiter
>() {}
// ============================================
// 3. In-memory rate limiter implementation
// ============================================
const makeRateLimiter = (config: RateLimitConfig) =>
Effect.gen(function* () {
const state = yield* Ref.make(HashMap.empty<string, RateLimitState>())
const getOrCreateState = (key: string, now: number) =>
Ref.modify(state, (map) => {
const existing = HashMap.get(map, key)
if (existing._tag === "Some") {
// Check if window expired
if (now >= existing.value.resetAt) {
// Start new window
const newState: RateLimitState = {
count: 0,
resetAt: now + config.windowMs,
}
return [newState, HashMap.set(map, key, newState)]
}
return [existing.value, map]
}
// Create new entry
const newState: RateLimitState = {
count: 0,
resetAt: now + config.windowMs,
}
return [newState, HashMap.set(map, key, newState)]
})
const incrementCount = (key: string) =>
Ref.modify(state, (map) => {
const existing = HashMap.get(map, key)
if (existing._tag === "Some") {
const updated = { ...existing.value, count: existing.value.count + 1 }
return [updated.count, HashMap.set(map, key, updated)]
}
return [1, map]
})
const limiter: RateLimiter = {
check: (key) =>
Effect.gen(function* () {
const now = Date.now()
const currentState = yield* getOrCreateState(key, now)
if (currentState.count >= config.maxRequests) {
const retryAfter = Math.ceil((currentState.resetAt - now) / 1000)
return yield* Effect.fail(
new RateLimitExceededError({
retryAfter,
limit: config.maxRequests,
})
)
}
yield* incrementCount(key)
}),
getStatus: (key) =>
Effect.gen(function* () {
const now = Date.now()
const currentState = yield* getOrCreateState(key, now)
return {
remaining: Math.max(0, config.maxRequests - currentState.count),
resetAt: currentState.resetAt,
}
}),
}
return limiter
})
// ============================================
// 4. Rate limit middleware
// ============================================
const withRateLimit = <A, E, R>(
handler: Effect.Effect<A, E, R>
): Effect.Effect<
A | HttpServerResponse.HttpServerResponse,
E,
R | RateLimiterService | HttpServerRequest.HttpServerRequest
> =>
Effect.gen(function* () {
const request = yield* HttpServerRequest.HttpServerRequest
const rateLimiter = yield* RateLimiterService
// Use IP address as key (in production, might use user ID or API key)
const clientKey = request.headers["x-forwarded-for"] || "unknown"
const result = yield* rateLimiter.check(clientKey).pipe(
Effect.matchEffect({
onFailure: (error) =>
Effect.succeed(
HttpServerResponse.json(
{
error: "Rate limit exceeded",
retryAfter: error.retryAfter,
},
{
status: 429,
headers: {
"Retry-After": String(error.retryAfter),
"X-RateLimit-Limit": String(error.limit),
"X-RateLimit-Remaining": "0",
},
}
)
),
onSuccess: () => handler,
})
)
return result
})
// ============================================
// 5. Usage example
// ============================================
const RateLimiterLive = Layer.effect(
RateLimiterService,
makeRateLimiter({
maxRequests: 100, // 100 requests
windowMs: 60 * 1000, // per minute
})
)
const apiEndpoint = withRateLimit(
Effect.gen(function* () {
// Your actual handler logic
return HttpServerResponse.json({ data: "Success!" })
})
)
What this file has done since we first saw it
Hashed on every crawl. A supply-chain change to an agent config is a question of when, not whether, so the history is kept rather than the latest state alone.
- yesterday First seen · 198 lines · 1,218 tokens per session scan A 957648beef79
add-rate-limiting-to-apis is a cursor rule published in the GitHub repository PaulJPhilp/EffectPatterns (794 stars, last pushed 2mo ago), licensed MIT. It adds 1,218 tokens to every session, about $0.0061 per session on Opus 5. A static security scan graded it A with 0 findings. No closer match exists in the catalogue, so it is treated as the original; first seen 2026-08-30.
Other cursor rules, from other repositories
transcreveai
Required handoff contract for nested TranscreveAI executions.
cursorrules
You are an expert software engineer and architect. You are part of a team, but your memory is reset after every session. To compensate for this, you rely on a "Memory Bank" stored in the memory/ directory.
typescript-coding-style
Enforces code style and best practices for TypeScript files.
javascript-typescript-code-style
Rules for JavaScript and TypeScript code style, including modern features, functional patterns, and descriptive naming conventions.
astro-development-guidelines
Enforces specific development guidelines for Astro projects, including TypeScript strictness and TailwindCSS usage.
convex-development---general
Applies general rules for Convex development, emphasizing schema design, validator usage, and correct handling of system fields.