はじめに

今回はCursorにおけるAIエージェントの制御方法について紹介します。
特に、.mdcファイル(マルチドキュメントコンテキスト)を使用して
AIアシスタントの動作をプロジェクト固有のルールに従わせる方法について説明します。

AIは毎回新しいコンテキストで動作するため、プロジェクト固有のルールを
明示的に伝える必要があります。
この課題を解決するために、.mdcファイルを使用して
AIが一貫したコーディング規約に従い、プロジェクトの品質を向上させることができます。

.mdcファイルとは

.mdcファイルは、CursorのAIアシスタントがコード補完、生成、リファクタリングを行う際に
参照するプロジェクト固有のルールやガイドラインを定義するためのファイルです。
mdcファイルを使うことで毎回プロジェクトのルールを伝える必要が無くなり
AIがステートレス(状態を保持しない)であるという課題を解決できます。

また、.mdcのルールはユーザールールと異なり常にコンテキストに含まれないため
必要な時のみ適用され、コンテキストの使用量を抑えることができます。

.mdcファイルの構造

.mdcファイルは、YAMLフロントマターとMarkdown本文の2つの部分で構成されています。

YAMLフロントマター(メタデータ部分)

ファイルの先頭にYAML形式でメタデータを記述します。
この部分で、ルールがいつ適用されるかを制御します。

---
description: "TypeScriptで関数の戻り値に明示的な型定義を強制する"
globs: ["**/*.ts", "**/*.tsx"]
priority: 1
version: "1.0"
tags: ["typescript", "conventions"]
alwaysApply: false
---

主要なフィールドは以下の通りです。

  • description: ルールの説明(120文字未満
  • globs: 適用するファイルパターン(例:["**/*.ts"]
  • priority: 優先度(1-5、1が最高)
  • version: ルールのバージョン
  • tags: ルールにタグを付けて分類・管理るためのフィールド
  • alwaysApply: 常時適用するかどうか(true/false)

Markdown本文(指示部分)

YAMLフロントマターの後に、具体的な指示をMarkdown形式で記述します。

Markdown本文の例:

# TypeScript型定義ルール

## Context
TypeScriptプロジェクトにおいて、関数の戻り値の型を明示的に定義する必要がある。

## Requirements
- すべての関数で戻り値の型を明示的に定義する
- any型の使用を避ける
- インターフェースや型エイリアスを適切に使用する

## Examples

<example>
// 正しい例
function calculateSum(a: number, b: number): number {
  return a + b;
}

interface User {
  id: number;
  name: string;
}

function getUser(id: number): User {
  return { id, name: "John" };
}
</example>

<example type="invalid">
// 避けるべき例
function calculateSum(a: number, b: number) {
  return a + b; // 戻り値の型が明示されていない
}

function processData(data: any): any {
  return data; // any型を使用している
}
</example>

ルールファイルの命名規則

.mdcファイルの名前は、管理しやすくするために以下の規則に従うことが推奨されています。

  • 001-Your-Rule.mdcのように数字で始める形式
  • 数字部分でルールの優先順位を設定
  • ハイフンで区切って説明的な名前を付ける

例:
001-typescript-conventions.mdc
002-react-best-practices.mdc
003-api-design.mdc

ルールの配置と階層構造

Cursorでは、異なるスコープでルールを適用できます。

1. プロジェクトルール(推奨)

プロジェクト固有のルールは、.cursor/rules/ディレクトリに配置します。
このディレクトリとその中の.mdcファイルはGitなどのバージョン管理ツールで管理し
チーム全体で共有します。
これにより、チームメンバー全員が同じAI制御ルールの下で作業でき
一貫したコーディング規約を維持できます。

プロジェクトルート/
├── .cursor/
│   └── rules/
│       ├── 001-typescript-conventions.mdc
│       ├── 002-react-best-practices.mdc
│       └── 003-api-design.mdc
└── src/

2. ユーザールール

個人の開発環境全体に適用されるルールです。
設定ファイルで定義され、常にコンテキストに含まれます。

3. チームルール

チーム・プランまたはエンタープライズ・プランで利用可能で
ダッシュボードから一元管理されます。

ルールの適用パターン

自動添付(Auto Attached)

globsフィールドを定義したルールは、指定されたファイルパターンが
参照された際に自動的にコンテキストにロードされます。

---
globs: ["**/*.ts", "**/*.tsx"]
---

エージェント要求(Agent Requested)

descriptionフィールドを定義したルールは、ユーザーのプロンプトの内容に基づき
AIエージェント自身がそのルールを含めるかどうかを決定します。

---
description: "Reactコンポーネントの命名規則に従う"
---

注意: globsdescriptionの使い分けについて
globsのみを定義した場合:指定されたファイルパターンが参照された際に自動的に適用されます
descriptionのみを定義した場合:AIエージェントが必要と判断した場合に適用されます
– 両方を定義した場合:globsによる自動適用がdescriptionよりも優先されるようです

実践的なルール作成例

Reactコンポーネントのルール

---
description: "Reactコンポーネントの作成と命名規則"
globs: ["**/*.tsx", "**/*.jsx"]
priority: 2
version: "1.0"
tags: ["react", "components"]
alwaysApply: false
---

Markdown本文の例:

# Reactコンポーネントルール

## Context
Reactプロジェクトにおけるコンポーネントの作成と命名に関する規約

## Requirements
- コンポーネント名はPascalCaseを使用する
- ファイル名はコンポーネント名と同じにする
- 関数コンポーネントを使用する
- Propsの型定義を明示的に行う

## Examples

<example>
// UserProfile.tsx
interface UserProfileProps {
  userId: number;
  userName: string;
}

export function UserProfile({ userId, userName }: UserProfileProps) {
  return (
    <div>
      <h2>{userName}</h2>
      <p>ID: {userId}</p>
    </div>
  );
}
</example>

<example type="invalid">
// user-profile.tsx(ファイル名がケバブケース)
interface Props {
  userId: number;
  userName: string;
}

export function userProfile(props: Props) { // コンポーネント名がcamelCase
  return (
    <div>
      <h2>{props.userName}</h2>
      <p>ID: {props.userId}</p>
    </div>
  );
}
</example>

API設計のルール

---
description: "RESTful APIの設計とエラーハンドリング"
globs: ["**/api/**/*.py", "**/routes/**/*.py"]
priority: 2
alwaysApply: false
---

Markdown本文の例:

# API設計ルール

## Context
FastAPIを使用したRESTful APIの設計に関する規約

## Requirements
- エンドポイントは名詞を使用し、動詞は避ける
- HTTPステータスコードを適切に使用する
- エラーレスポンスは統一された形式で返す
- リクエストとレスポンスの型を明示的に定義する

## Examples

<example>
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel

app = FastAPI()

class UserCreate(BaseModel):
    name: str
    email: str

class UserResponse(BaseModel):
    id: int
    name: str
    email: str

@app.post("/users", response_model=UserResponse, status_code=201)
async def create_user(user: UserCreate):
    # ユーザー作成処理
    return UserResponse(id=1, name=user.name, email=user.email)

@app.get("/users/{user_id}", response_model=UserResponse)
async def get_user(user_id: int):
    if user_id <= 0:
        raise HTTPException(status_code=400, detail="Invalid user ID")
    # ユーザー取得処理
    return UserResponse(id=user_id, name="John", email="john@example.com")
</example>

<example type="invalid">
# 避けるべき例
@app.post("/createUser")  # 動詞を使用
async def create_user(user_data: dict):  # 型定義なし
    return {"user_id": 1, "name": user_data["name"]}  # 統一されていないレスポンス形式
</example>

まとめ

.mdcファイルを使用することで、CursorのAIアシスタントを
プロジェクト固有のルールに従わせることができます。
この方法の最大の利点は、AIが一貫したコーディング規約に従い
プロジェクトの品質を向上させることができる点です。

プロジェクトの規模や複雑さに応じて、適切な粒度でルールを作成し
AIアシスタントを効果的に活用してみてください。