# Cursor Skill (.mdc) Authoring

> Author effective Cursor rules in .cursor/rules/*.mdc - YAML frontmatter (description, globs, alwaysApply), the four rule types, scoped QA rules, subagents, and structure that actually steers the model.

- Skill: `pramoddutta/cursor-skill-mdc-authoring` (Agent Skill)
- Install (CLI): `npx skillmds@latest add pramoddutta/cursor-skill-mdc-authoring`
- Raw SKILL.md: https://api.skillmd.com/api/skills/pramoddutta/cursor-skill-mdc-authoring/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: AI & ML
- License: MIT
- Author: PramodDutta (https://skillmd.com/u/pramoddutta)
- Updated: 2026-09-10
- Page: https://skillmd.com/skills/pramoddutta/cursor-skill-mdc-authoring

---


# Cursor Skill (.mdc) Authoring Skill

You are an expert at writing Cursor rules - the `.mdc` files in `.cursor/rules/` that steer Cursor's AI. When the user asks you to create or improve a Cursor rule (especially for testing/QA conventions), you write a correctly typed `.mdc` with valid YAML frontmatter, scope it precisely with globs, keep it short and imperative, and reference example files instead of pasting walls of code. You never write one giant catch-all rule; you write small, composable, well-scoped rules.

## Core Principles

1. **`.mdc` lives in `.cursor/rules/`, not the legacy root file.** Modern Cursor uses `.cursor/rules/*.mdc`. Nested rules in subfolders (`packages/api/.cursor/rules/`) scope to that part of the tree.
2. **The frontmatter fields decide *when* the rule fires.** `description`, `globs`, and `alwaysApply` together select one of four rule types. Get these wrong and the rule never activates - or always does.
3. **Four rule types, one chosen by frontmatter.** Always, Auto-Attached, Agent-Requested, and Manual. Pick deliberately; do not default everything to `alwaysApply: true`.
4. **`description` is the trigger for Agent-Requested rules.** When `alwaysApply` is false and `globs` is empty, the model reads `description` to decide relevance. Write it as a clear "use this when..." sentence.
5. **Scope with `globs`, not prose.** A rule that only matters for tests should be glob-scoped to `**/*.test.ts`, not a paragraph saying "only apply to tests."
6. **Keep rules short and imperative.** Under ~500 lines, ideally far less. Bullet directives beat essays. The rule competes for the model's attention with the actual task.
7. **Reference files with `@`, do not paste them.** `@tests/example.spec.ts` pulls in a canonical example on demand instead of bloating every prompt.
8. **One concern per rule file.** A "Playwright conventions" rule and a "PR description format" rule are two files. Composable rules are easier to scope and maintain.

## The Four Rule Types

| Type | `alwaysApply` | `globs` | `description` | When it fires |
|---|---|---|---|---|
| Always | `true` | - | - | Injected into every prompt in the project |
| Auto-Attached | `false` | set | optional | When a file matching `globs` is in context |
| Agent-Requested | `false` | empty | required | When the model judges it relevant from the description |
| Manual | `false` | empty | empty | Only when invoked explicitly with `@ruleName` |

## File Layout

```
project/
  .cursor/
    rules/
      000-core.mdc                # Always: project-wide non-negotiables
      testing-playwright.mdc      # Auto-Attached: globs **/*.spec.ts
      testing-pytest.mdc          # Auto-Attached: globs tests/**/*.py
      pr-format.mdc               # Agent-Requested: description-triggered
      legacy-migration.mdc        # Manual: invoked with @legacy-migration
  packages/
    api/
      .cursor/rules/
        api-contracts.mdc         # nested: scoped to the api package
```

## Anatomy of an .mdc File

```mdc
---
description: TypeScript end-to-end test conventions using Playwright.
globs: ["**/*.spec.ts", "tests/**/*.ts"]
alwaysApply: false
---

# Playwright E2E Conventions

When writing or editing Playwright tests in this project:

- Use `getByRole`, `getByLabel`, `getByText` locators. Do NOT use raw XPath.
- Never use `page.waitForTimeout`. Rely on auto-wait and web-first assertions.
- Assert with `await expect(locator).toBeVisible()` - never fetch a value
  manually and compare it.
- One `test()` verifies one behavior. Use `test.step()` to label phases.
- Page Objects live in `tests/pages/*.page.ts` with `Locator` fields built
  in the constructor.

See the canonical example: @tests/pages/login.page.ts
```

The frontmatter here makes it Auto-Attached: it activates whenever a `.spec.ts` file is open or referenced, without the model having to decide.

## Rule Type 1: Always (project core)

Use sparingly - it costs context on every single prompt. Reserve it for genuinely universal rules.

```mdc
---
alwaysApply: true
---

# Project Core Rules

- Package manager is pnpm. Never suggest `npm install` or `yarn`.
- TypeScript strict mode is on. No `any` without an inline justification comment.
- Tests are required for every new function in `src/`. No PR merges without them.
- Follow Prettier: single quotes, semicolons, 100-char width.
```

## Rule Type 2: Auto-Attached (the workhorse for QA)

This is the right type for most testing conventions: it fires exactly when a matching test file is in play.

```mdc
---
description: Python test conventions with pytest.
globs: ["tests/**/*.py", "**/test_*.py"]
alwaysApply: false
---

# Pytest Conventions

- Use fixtures from `conftest.py`; never write unittest setUp/tearDown.
- Default fixtures to function scope; widen only for expensive read-only resources.
- Parametrize cases with `@pytest.mark.parametrize` and always pass `ids=`.
- Mock at the point of use with the `mocker` fixture: patch where the name is
  looked up, not where it is defined.
- Name tests `test_<unit>_<condition>_<expected>`.
- All markers must be declared in pyproject.toml (suite runs with --strict-markers).

Reference fixture setup: @tests/conftest.py
```

## Rule Type 3: Agent-Requested (description-triggered)

No globs - the model reads `description` and decides. Write the description as an explicit trigger.

```mdc
---
description: >
  Use when writing a pull request title or description. Enforces the
  Conventional Commits style and the required QA checklist.
alwaysApply: false
---

# PR Description Format

When asked to draft a PR:

- Title: `type(scope): summary` (feat, fix, test, refactor, chore).
- Body must include a "## Testing" section listing how the change was verified.
- Include a checklist: [ ] unit tests, [ ] e2e if UI changed, [ ] no skipped tests.
- Link the issue with `Closes #<n>`.
```

## Rule Type 4: Manual (invoked on demand)

Empty `description` and `globs`. Fires only when the user types `@rule-name`. Good for heavy, occasional workflows.

```mdc
---
description:
globs:
alwaysApply: false
---

# Legacy Selenium -> Playwright Migration

(Invoke with @legacy-migration when porting an old test.)

- Map By.id/By.cssSelector to getByLabel/getByRole.
- Delete every WebDriverWait; replace with web-first `expect` assertions.
- Convert the Java Page Object to a TS class with lazy Locator fields.
- Keep both suites green; migrate one page at a time.
```

## Subagents: Delegating QA Work

Cursor can spawn subagents for focused, parallel work. Define them so a parent agent can hand off bounded tasks (e.g., "write the tests" while the main thread keeps building). Keep each subagent single-purpose with an explicit success criterion.

```mdc
---
description: Use to spin up a focused test-writing subagent for a single module.
alwaysApply: false
---

# Test-Author Subagent

When delegating test creation, instruct the subagent to:

1. Read ONLY the target module and its existing tests.
2. Write tests following @.cursor/rules/testing-pytest.mdc (or the matching
   framework rule for the file type).
3. Cover: happy path, each error branch, and boundary inputs.
4. Run the suite and report pass/fail counts. Do NOT edit source code.
5. Success criterion: new tests pass and branch coverage for the module is >= 85%.

Keep the subagent's scope to one module so its context stays small and focused.
```

## Writing Effective QA Rules

A rule that steers the model well is concrete, scoped, and shows an example.

```mdc
---
description: API contract test conventions.
globs: ["**/*.contract.test.ts"]
alwaysApply: false
---

# API Contract Tests

- Validate responses against the Zod schema in `@src/schemas`, not ad-hoc asserts.
- Test the status code AND the body shape for every endpoint.
- Include at least one 4xx case (bad input) per endpoint.
- Use the shared `apiClient` fixture; never construct fetch calls inline.

Good:
\`\`\`ts
const res = await apiClient.post('/orders', { amount: -1 });
expect(res.status).toBe(422);
expect(() => OrderSchema.parse(res.body)).toThrow();
\`\`\`

Bad: asserting only `expect(res.status).toBe(200)` with no body validation.
```

## Best Practices

1. **Choose the rule type on purpose.** Default to Auto-Attached for anything file-specific (most QA rules). Reserve Always for true universals; it taxes every prompt.
2. **Make `description` an explicit "use when..." sentence for Agent-Requested rules.** The model triggers on it - vague descriptions never fire.
3. **Scope with `globs`, not prose.** `globs: ["**/*.spec.ts"]` is enforceable; "apply only to test files" written in the body is a hope.
4. **Keep each rule under ~500 lines and single-concern.** Short, imperative bullets win attention; one topic per file keeps scoping clean.
5. **Reference canonical files with `@path`.** Pull in one good example on demand instead of pasting code into every prompt.
6. **Number Always rules (`000-core.mdc`) to control ordering.** Predictable load order avoids surprising overrides.
7. **Use nested `.cursor/rules/` for monorepos.** A package-local rule scopes naturally to that package's tree.
8. **Give subagents a bounded scope and an explicit success criterion.** "Write tests for this one module, do not touch source, target 85% coverage" keeps delegation reliable.

## Anti-Patterns to Avoid

1. **`alwaysApply: true` on everything.** It floods every prompt with irrelevant rules and dilutes the ones that matter. Scope instead.
2. **Agent-Requested rule with a vague or empty `description`.** With no globs and no clear description, the model has nothing to trigger on - the rule is dead.
3. **One giant `rules.mdc` covering testing, style, git, and architecture.** Unscopable and unmaintainable. Split into focused files.
4. **Pasting whole example files into the rule body.** It balloons context. Use `@file` references.
5. **Writing scope as prose instead of globs.** "Only for tests" in the body still loads the rule everywhere; the model may apply it to non-test files.
6. **Essays and rationale dumps.** Rules are directives, not documentation. State what to do, not three paragraphs of why.
7. **Subagents with open-ended scope.** "Improve the tests" with no boundary lets a subagent wander into source files and rack up context. Bound it.

## When to Trigger This Skill

Trigger when the user asks to:
- Create or edit a Cursor rule, `.mdc` file, or `.cursor/rules/` config
- Set up "rules for AI" / coding conventions for Cursor
- Choose between Always / Auto-Attached / Agent-Requested / Manual rule types
- Write QA or testing conventions that Cursor should follow
- Configure Cursor subagents for delegated test work
- Fix a Cursor rule that never activates or always activates

This skill is specific to Cursor's `.mdc` rule format. For authoring portable SKILL.md skills (this directory's format) or Claude Code skills, use the relevant skill-authoring guidance instead.

