Verified against Cursor · 2026-08-08
Author a Cursor Project Rule that actually gets applied instead of ignored
A brief for writing a .cursor/rules/*.mdc Project Rule with the correct rule type, glob scope, and description for triggering, so the rule reliably activates in the situations it's meant for instead of sitting in the repo unused because it was scoped or worded wrong.
The prompt
Ready to copy — highlighted parts are example details you can swap.
Write a Cursor Project Rule as a .mdc file for this repository. Before writing the body, choose the correct rule type deliberately — this determines whether the rule fires at all: RULE PURPOSE Every new API route must validate its input with a Zod schema before touching business logic, never trust req.body directly. WHEN THIS RULE SHOULD APPLY Any time a new file is added under app/api/**/route.ts, or an existing route handler is modified. RULE TYPE — pick exactly one and justify it in one line - Always: applied to every agent request in this repo regardless of what file is open. Use only for genuinely universal constraints. - Auto Attached: applied when a file matching a glob pattern is in context. Use for stack- or directory-specific conventions. - Agent Requested: the agent decides whether to pull this rule in, based on the rule's description matching the current task. Use for optional guidance that shouldn't always be in context. - Manual: only applied when explicitly @-mentioned. Use for rules that are situational enough that auto-triggering would be noise. CONTENT THIS RULE MUST ENFORCE Define a Zod schema per route named <RouteName>Schema, call .parse() on req.body/searchParams before any other logic runs, and return a 400 with the Zod error details on failure — never a generic 500. EXAMPLES OF THE PATTERN BEING FOLLOWED CORRECTLY, AND ONE COUNTEREXAMPLE Correct: OrderCreateSchema.parse(req.body) at the top of the handler, wrapped in try/catch returning 400 on ZodError. Counterexample: reading req.body.email directly three lines into the handler before any validation — this is exactly what the rule exists to prevent, even if the rest of the handler is otherwise correct. RULES FOR WRITING THE RULE ITSELF - If you chose Auto Attached, write a glob pattern precise enough to match the intended files without also matching unrelated ones — test it mentally against at least one file that should match and one that shouldn't. - If you chose Agent Requested, write the description field as a specific trigger condition an agent would recognize in a task, not a vague summary — "apply when writing or modifying a database migration" not "database-related rules." - Keep the rule body itself short and directive, not a general essay about best practices — a rule the agent has to read in full every time it's pulled into context should be the length that's actually worth that cost. - State what to do, not just what to avoid — "use the shared Logger class" reads as an instruction; "don't use console.log" only tells the agent what's forbidden, not what replaces it. - Include the one counterexample given above inside the rule itself if it clarifies a boundary case that the positive examples alone wouldn't rule out. Output the complete .mdc file, including its front matter (description, globs, alwaysApply as appropriate for the chosen type) and the rule body.
Customize
Optional — swap in your own details for the highlighted parts above.
Why this works
Cursor's Project Rules system deliberately splits rules into four trigger types precisely because a single global rules file — the older .cursorrules convention — either gets loaded into every single request regardless of relevance, bloating context and diluting the signal of any one rule, or gets skipped entirely once a codebase has enough rules that no one maintains the one file anymore. Forcing an explicit choice among Always, Auto Attached, Agent Requested, and Manual, justified in one line, is what actually determines whether a rule fires in the situation it's meant for: a rule about API-route validation written as Always fires on every unrelated request including a CSS tweak, silently wasting context budget on every single turn, while the same rule written as Auto Attached with a glob scoped to app/api/**/route.ts fires exactly when it's relevant and never otherwise. The requirement to mentally test a glob against a matching and a non-matching file catches the most common authoring mistake with Auto Attached rules — a pattern so broad it also attaches to test files or generated files where the rule doesn't apply, which produces exactly the same context-bloat problem the rule type was meant to avoid, just scoped slightly narrower than Always. For Agent Requested rules, the description field is not documentation — it's the literal string an agent compares against the current task to decide whether to pull the rule in at all, so a vague description like "database-related rules" fails to trigger on a task described as "add a migration," while a description written as the actual trigger condition — "apply when writing or modifying a database migration" — matches the vocabulary a task description would actually use. Requiring the rule to state what to do rather than only what to avoid matters because a rule that only forbids something gives the agent no positive pattern to fall back on, and it will substitute its own default, which is very often not what the rule's author actually wanted in place of the forbidden pattern.
What you get back
--- description: Apply when creating or modifying an API route handler under app/api globs: app/api/**/route.ts alwaysApply: false ---
# API Route Input Validation
Every route handler must validate input with a Zod schema before running any business logic.
- Define a schema named <RouteName>Schema in the same file or an adjacent schemas.ts.
- Call .parse() on req.body or searchParams as the first line of the handler body.
- On ZodError, return a 400 with the error's flattened issues — never let validation fall through to a generic try/catch that returns 500.
Counterexample (do not do this): reading req.body.email directly before .parse() runs, even if validation happens later in the function. The read itself is the violation, regardless of what happens after it.
Use the shared Logger class for any validation-failure logging, not console.log — see logging-conventions.mdc.
Verified against
Cursor 2.2 · 2026-08-08
Changelog
- 2026-08-08 — Initial publish, verified against Cursor 2.2's Project Rules (.cursor/rules/*.mdc) system.
Need this built into your business?
If a prompt isn't enough — custom software, built and maintained for you — that's Scult's day job.
EXPLORE CUSTOM SOFTWARE
