If you've spent time writing .cursorrules and Cursor's agent mode keeps ignoring your conventions — this is not a bug in your rules. It's a format mismatch you were never warned about.
Cursor has two separate rule-loading systems. Agent mode reads only one of them. .cursorrules feeds the other.
This guide covers the exact migration: what to change, where files go, and four concrete .mdc examples you can drop in today.
The format split: what reads what
| Attribute | .cursorrules |
.mdc (Project Rules) |
|---|---|---|
| Location | Project root | .cursor/rules/*.mdc |
| Used by | Ask mode, Edit mode | Agent mode |
| Frontmatter required | No | Yes — YAML block with --- delimiters |
| Scope control | None (whole project) | Per-file via globs:
|
| Loaded automatically | Yes | Only with alwaysApply: true
|
| Agent mode compliance | ❌ Ignored | ✅ Active |
The architectural split exists because .cursorrules predates Cursor's agent-mode context pipeline. It wasn't deprecated — it still works in Ask/Edit mode. But agent mode runs on a different context loader that only reads .mdc files from .cursor/rules/.
You can confirm this by opening a new agent session and asking it to name the active project rules. If you only have .cursorrules, it will say none.
The .mdc frontmatter format
Every .mdc file needs a YAML frontmatter block. The three fields that matter:
---
description: "What this rule file covers"
alwaysApply: true
globs: ["**/*.ts", "**/*.tsx"]
---
-
description— shown in Cursor's rules panel, used for context relevance scoring -
alwaysApply—truemeans load on every agent run;falsemeans only when the glob matches the active file -
globs— array of glob patterns scoping the rule to specific file types; omit for global rules
The combination of alwaysApply: true + a specific glob is the most reliable setup: the rule loads always, but gets treated as high-relevance when matching files are in context.
Four migration examples from the Gist
The migration Gist has four ready-to-use .mdc files. Here's what each one does and why it's structured that way.
1. typescript.mdc — strict-mode enforcement
---
description: TypeScript strict-mode enforcement
globs: ["**/*.ts", "**/*.tsx", "!**/*.test.ts", "!**/*.spec.ts"]
alwaysApply: true
---
# TypeScript Rules
- tsconfig must have "strict": true. Never disable strict checks to silence errors.
- No any. Use unknown + type guards, or as Type only at I/O boundaries.
- Every exported function needs an explicit return type, including async functions (Promise<T>).
- Model state as discriminated unions: { status: "loading" } | { status: "success"; data: T } | { status: "error"; error: E }
- Use Readonly<T> for data that should not be mutated. Prefer as const for config objects.
- Validate env vars at startup with zod or equivalent. Never access process.env.FOO in business logic.
Note the glob exclusion for test files (!**/*.test.ts). Test files legitimately need looser typing (mocks, stubs). Excluding them prevents false compliance failures.
2. no-any.mdc — single-concern rule file
---
description: Enforce no-any TypeScript rule
globs: ["**/*.ts", "**/*.tsx"]
alwaysApply: true
---
# No Any Rule
- Never use the any type. If you need to accept unknown input, use unknown and narrow it.
- After JSON.parse(), cast to unknown first, then validate with a type guard or zod before use.
- If a third-party type is wrong, use a local override type file — do not cast with any.
- Replace any[] with unknown[] or a specific union type.
- Record<string, any> → Record<string, unknown> or a typed value.
- The only permitted exception: testing mocks where the type is genuinely irrelevant to the test.
This exists as a separate file from typescript.mdc deliberately. Short, single-concern files have higher pickup rates than long multi-rule files. When agent mode compacts context, it evaluates relevance per file — a short file about one topic scores higher than a long file about many topics.
3. react.mdc — framework-specific rules
---
description: React + Next.js App Router production rules
globs: ["**/*.tsx", "**/*.jsx", "src/**/*.ts"]
alwaysApply: true
---
# React + Next.js Rules
- Default to Server Components. Add "use client" only when you need hooks, browser APIs, or event handlers.
- Fetch data in Server Components or page.tsx. Never fetch in useEffect.
- One component per file. File name = component name (PascalCase).
- Every route with async data needs a sibling loading.tsx and error.tsx.
- Use next/image instead of img. Use next/font instead of Google Fonts link tags.
- Define an explicit Props interface for every component. No any.
- Use server actions for mutations — not API routes called from client components.
The glob src/**/*.ts is there to catch server action files, which are .ts but live in the src tree alongside React components. Without it, a server action file triggers only the TypeScript rules, not the App Router conventions.
4. api-errors.mdc — scoped to API layer
---
description: API error handling patterns
globs: ["**/api/**/*.ts", "**/routes/**/*.ts", "**/handlers/**/*.ts", "**/*.controller.ts"]
alwaysApply: false
---
# API Error Handling Rules
- Never return raw Error objects in API responses. Map to a consistent error schema: { error: string, code: string }.
- HTTP status codes must be intentional: 400 (client error), 401 (auth), 403 (authz), 404 (not found), 422 (validation), 500 (server).
- Log errors with context at the handler layer. Include request ID, user ID if available, and the original error.
- Validation errors (400/422) include the specific field and constraint that failed.
- Never expose stack traces or internal error messages to API consumers.
- Use Result<T, E> or explicit error returns instead of throwing in service layer functions.
This one uses alwaysApply: false. It's only relevant when editing API route files, so there's no reason to load it into every agent run. The glob is specific enough that Cursor will load it exactly when needed.
The migration procedure
Step 1 — Create the rules directory:
mkdir -p .cursor/rules
Step 2 — Split, don't copy. If your .cursorrules is 80 lines covering TypeScript, React, and API conventions, don't paste it as one .mdc file. Split it into typescript.mdc, react.mdc, api.mdc. One domain per file.
Step 3 — Add frontmatter to each file:
---
description: "<what this file governs>"
alwaysApply: true
globs: ["<pattern matching files this rule applies to>"]
---
Step 4 — Rewrite negative rules as positive. "Never use var" → "Use const or let only." "Don't use any" → "All types must be explicit." Positive imperatives have higher compliance in agent mode.
Step 5 — Keep each file under 50 lines. Long files get silently deprioritized during context compaction. If a domain needs more than 50 lines of rules, split it into two files.
Step 6 — Verify the migration:
for f in .cursor/rules/*.mdc; do
echo "=== $f ==="
head -6 "$f"
echo ""
done
Every file should show description: and alwaysApply: in the first six lines. If a file starts with rule text — no --- delimiter — it won't load.
Step 7 — Rename .cursorrules: Don't delete it yet. Rename to .cursorrules.bak. If Ask/Edit mode behavior changes, you'll know why and you can reference the original. Delete after a week of confirming agent mode is working.
What the migration fixes
Rules that previously had zero effect in agent mode start working immediately after migration — assuming they meet the format requirements:
- Lives in
.cursor/rules/as.mdc - Has
alwaysApply: truein frontmatter - Has a
description:field - Has scoped
globs:matching the target file types - Is under 50 lines
- Each rule is a single imperative sentence
Most .cursorrules files fail the first three by definition. That's the entire problem.
Free: grab the 4 .mdc files
The four files above are in the Cursor rules migration Gist — download them, drop into .cursor/rules/, and your TypeScript/React/API conventions are active in agent mode immediately.
If you need rules for other stacks (Go, Python, Rust, Node, Next.js, Svelte, and more) — the Cursor Rules Pack covers 50 production-tested rules across 14 stacks, pre-structured in .mdc format with correct frontmatter, glob scoping, and agent-mode compliance built in. One-time $27, instant download.
Top comments (0)