メインコンテンツへスキップ
注目 MCP TypeScript チュートリアル 実践 テスト

初めての MCP Server を構築:Director Mode Assistant

実用的な MCP Server を構築しながら MCP 開発を学びましょう。TDD、Auto-Cycle、SpecKit の概念をデモンストレーション。TypeScript、テスト、ベストプラクティスが含まれています。

2026年1月15日 15分で読める 著者:Claude World

🎯 構築するもの

このチュートリアルでは、3つのコア Director Mode 概念を実証する完全な MCP Server をゼロから構築します:

  • TDD サイクル管理 - Red-Green-Refactor ワークフローの追跡とガイダンス
  • Auto-Cycle 進度状況 - 自動化された開発イテレーションの監視
  • SpecKit 検証 - 仕様と実装の一貫性の検証

このサーバーには 9 つのツール3 つのリソース が含まれ、完全な TypeScript サポート、包括的なテスト、詳細なドキュメントが提供されます。

最終プロジェクト: github.com/claude-world/mcp-director-mode-server


📚 前提条件

開始する前に、以下のことを確認してください:

# Node.js バージョンを確認(18+が必要)
node --version  # v18.0.0 以降

# TypeScript を確認
npm install -g typescript

# Claude Code MCP サポートを確認
claude --mcp list

必要な知識:

  • 基本的な TypeScript/JavaScript
  • コマンドラインツールの精通
  • TDD 概念の理解(あれば役立ちますが必須ではありません)

🚀 クイックスタート

ステップ 1:プロジェクトを初期化

新しいディレクトリを作成して初期化:

mkdir mcp-director-mode-server
cd mcp-director-mode-server

# npm プロジェクトを初期化
npm init -y

# 依存関係をインストール
npm install @modelcontextprotocol/sdk
npm install --save-dev typescript @types/node vitest

# TypeScript を初期化
npx tsc --init

package.json を更新:

{
  "name": "mcp-director-mode-server",
  "version": "1.0.0",
  "type": "module",
  "scripts": {
    "build": "tsc",
    "test": "vitest",
    "start": "node dist/index.js"
  }
}

ステップ 2:TypeScript を設定

tsconfig.json を作成:

{
  "compilerOptions": {
    "target": "ES2022",
    "module": "Node16",
    "moduleResolution": "Node16",
    "strict": true,
    "outDir": "./dist",
    "rootDir": "./src",
    "esModuleInterop": true,
    "skipLibCheck": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "tests"]
}

🛠️ MCP Server の構築

ステップ 3:サーバーエントリポイントを作成

src/index.ts を作成:

import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import {
  CallToolRequestSchema,
  ListToolsRequestSchema,
} from '@modelcontextprotocol/sdk/types.js';

// サーバーインスタンスを作成
const server = new Server(
  {
    name: 'director-mode-assistant',
    version: '1.0.0',
  },
  {
    capabilities: {
      tools: {},
      resources: {},
    },
  }
);

// ツールを登録(次に追加)
server.setRequestHandler(ListToolsRequestSchema, async () => ({
  tools: [
    {
      name: 'check_tdd_status',
      description: '現在の TDD サイクルの状態を確認(RED/GREEN/REFACTOR)',
      inputSchema: {
        type: 'object',
        properties: {
          projectPath: {
            type: 'string',
            description: 'プロジェクトディレクトリへのパス',
          },
        },
        required: ['projectPath'],
      },
    },
    // ... さらにツールをここに追加
  ],
}));

// ツール呼び出しを処理
server.setRequestHandler(CallToolRequestSchema, async (request) => {
  const { name, arguments: args } = request.params;

  switch (name) {
    case 'check_tdd_status':
      // 実装は次に追加
      return {
        content: [{
          type: 'text',
          text: 'TDD 状態:RED - テストが失敗しています',
        }],
      };

    default:
      throw new Error(`不明なツール:${name}`);
  }
});

// サーバーを起動
async function main() {
  const transport = new StdioServerTransport();
  await server.connect(transport);
  console.error('Director Mode Assistant MCP Server 実行中...');
}

main().catch(console.error);

ステップ 4:TDD ツールを実装

src/tools/tdd.ts を作成:

import { execSync } from 'child_process';
import { readFileSync, existsSync } from 'fs';
import { join } from 'path';

export interface TDDStatus {
  phase: 'RED' | 'GREEN' | 'REFACTOR';
  testCount: number;
  passingTests: number;
  failingTests: number;
  lastRun: Date;
}

export function checkTDDStatus(projectPath: string): TDDStatus {
  // テストファイルを確認
  const testDir = join(projectPath, 'tests');
  if (!existsSync(testDir)) {
    return {
      phase: 'RED',
      testCount: 0,
      passingTests: 0,
      failingTests: 0,
      lastRun: new Date(),
    };
  }

  // テストを実行して出力を解析
  try {
    const output = execSync('npm test', {
      cwd: projectPath,
      encoding: 'utf-8',
    });

    // テスト結果を解析(Jest の例)
    const match = output.match(/(\d+) passed, (\d+) failed/);
    if (match) {
      const passing = parseInt(match[1]);
      const failing = parseInt(match[2]);

      return {
        phase: failing > 0 ? 'RED' : 'GREEN',
        testCount: passing + failing,
        passingTests: passing,
        failingTests: failing,
        lastRun: new Date(),
      };
    }
  } catch (error: any) {
    // テストが失敗
    const output = error.stdout || error.message;
    const match = output.match(/(\d+) passed, (\d+) failed/);
    if (match) {
      return {
        phase: 'RED',
        testCount: parseInt(match[1]) + parseInt(match[2]),
        passingTests: parseInt(match[1]),
        failingTests: parseInt(match[2]),
        lastRun: new Date(),
      };
    }
  }

  return {
    phase: 'RED',
    testCount: 0,
    passingTests: 0,
    failingTests: 0,
    lastRun: new Date(),
  };
}

export function suggestTDDNextStep(status: TDDStatus): string {
  switch (status.phase) {
    case 'RED':
      return 'テストを通過させるための最小限のコードを書く';
    case 'GREEN':
      return 'すべてのテストが合格!リファクリングの時です';
    case 'REFACTOR':
      return 'リファクリング完了。テストを実行して破壊がないか確認';
    default:
      return '失敗するテストを書くことから始めましょう';
  }
}

ステップ 5:状態管理のためのリソースを追加

src/resources/index.ts を作成:

import { readFileSync, writeFileSync, existsSync } from 'fs';
import { join } from 'path';

export interface ProjectState {
  currentPhase: string;
  lastUpdate: Date;
  metrics: {
    totalTests: number;
    testPassRate: number;
    lastCommit: string;
  };
}

const STATE_FILE = '.director-mode-state.json';

export function getProjectState(projectPath: string): ProjectState {
  const statePath = join(projectPath, STATE_FILE);

  if (!existsSync(statePath)) {
    return {
      currentPhase: '初期化中',
      lastUpdate: new Date(),
      metrics: {
        totalTests: 0,
        testPassRate: 0,
        lastCommit: 'なし',
      },
    };
  }

  const data = readFileSync(statePath, 'utf-8');
  return JSON.parse(data);
}

export function updateProjectState(
  projectPath: string,
  updates: Partial<ProjectState>
): void {
  const current = getProjectState(projectPath);
  const updated = {
    ...current,
    ...updates,
    lastUpdate: new Date(),
  };

  const statePath = join(projectPath, STATE_FILE);
  writeFileSync(statePath, JSON.stringify(updated, null, 2));
}

🧪 MCP Server をテスト

ステップ 6:テストを作成

tests/tdd.test.ts を作成:

import { describe, it, expect } from 'vitest';
import { checkTDDStatus, suggestTDDNextStep } from '../src/tools/tdd.js';

describe('TDD ツール', () => {
  it('RED フェーズで失敗したテストを検出すること', () => {
    const status = {
      phase: 'RED',
      testCount: 10,
      passingTests: 7,
      failingTests: 3,
      lastRun: new Date(),
    };

    const suggestion = suggestTDDNextStep(status);
    expect(suggestion).toContain('最小限のコード');
  });

  it('GREEN フェーズでリファクリングを提案すること', () => {
    const status = {
      phase: 'GREEN',
      testCount: 10,
      passingTests: 10,
      failingTests: 0,
      lastRun: new Date(),
    };

    const suggestion = suggestTDDNextStep(status);
    expect(suggestion).toContain('リファク');
  });
});

テストを実行:

npm test

🚀 サーバーを実行

ステップ 7:ビルドしてテスト

# TypeScript をビルド
npm run build

# サーバーを起動
npm start

ステップ 8:Claude Code から接続

~/.claude.json を作成または更新:

{
  "mcpServers": {
    "director-mode": {
      "command": "node",
      "args": ["/path/to/mcp-director-mode-server/dist/index.js"]
    }
  }
}

Claude Code を再起動してサーバーが読み込まれたことを確認:

claude mcp list

📖 使用例

例 1:TDD 状態を確認

Claude Code で:

director-mode server を使って現在のプロジェクトの TDD 状態を確認して

Claude は check_tdd_status ツールを呼び出して報告:

現在の TDD フェーズ:GREEN
テスト:15 件合格、0 件失敗
次のステップ:コードをきれいにするリファクリングを検討

例 2:TDD ガイダンスを取得

RED フェーズにいます。次は何をすべきですか?

Claude はサーバーを使って提案:

テストを通過させる最小限のコードを書いてください。

今は完璧さを気にせず、テストを通すことに集中してください。
すべてのテストが緑色になったら、リファクリングできます。

🎓 実証されるコア MCP 概念

1. ツール設計

ツールは MCP サーバーが機能を公開する主な方法です:

{
  name: 'check_tdd_status',
  description: '現在の TDD サイクル状態を確認',
  inputSchema: {
    type: 'object',
    properties: {
      projectPath: { type: 'string' }
    },
    required: ['projectPath']
  }
}

ベストプラクティス

  • 明確で説明的な名前
  • 詳細な入力検証(JSON Schema)
  • Claude が理解するための役立つ説明

2. リソース管理

リソースは状態と設定を提供します:

export function getProjectState(projectPath: string): ProjectState
export function updateProjectState(path, updates): void

ベストプラクティス

  • セッションをまたぐ持久化状態
  • 不変更新(新しい状態を作成、既存を変更しない)
  • 互換性のための JSON シリアライゼーション

3. エラー処理

常にエラーを優雅に処理:

try {
  const output = execSync('npm test', { cwd: projectPath });
  return parseSuccess(output);
} catch (error) {
  return parseFailure(error.stdout);
}

ベストプラクティス

  • サーバーをクラッシュさせない
  • 意味のあるエラーメッセージを提供
  • デバッグのために stderr にエラーを記録

🏗️ プロジェクト構造

mcp-director-mode-server/
├── src/
│   ├── index.ts           # サーバーエントリポイント
│   ├── tools/
│   │   ├── tdd.ts         # TDD サイクルツール
│   │   ├── cycle.ts       # Auto-cycle ツール
│   │   └── spec.ts        # SpecKit ツール
│   └── resources/
│       └── index.ts       # 状態管理
├── tests/
│   ├── tdd.test.ts        # ツールテスト
│   └── integration.test.ts # 統合テスト
├── examples/
│   ├── basic-usage.md     # 使用例
│   └── tdd-workflow.md    # TDD ワークフローガイド
├── docs/
│   └── ARCHITECTURE.md    # 設計決定
├── package.json
├── tsconfig.json
└── vitest.config.ts

🚦 次のステップ

サーバーを強化

  1. さらにツールを追加

    • validate_refactor - リファクリングが安全か確認
    • generate_tests - テストスキャフォールドを生成
    • run_cycle_step - Auto-Cycle イテレーションを実行
  2. リソースを追加

    • project://state - 現在のプロジェクト状態
    • project://metrics - 品質メトリクスダッシュボード
    • cycle://history - Auto-Cycle 履歴ログ
  3. テストを改善

    • モック MCP クライアントでの統合テストを追加
    • エラーシナリオをテスト
    • 本物の Claude Code 接続での E2E テストを追加

サーバーを公開

# npm に公開
npm publish

# または GitHub で共有
git init
git add .
git commit -m "Initial MCP server"
git remote add origin https://github.com/YOUR_USERNAME/your-mcp-server.git
git push -u origin main

📚 さらに学ぶ


💡 現場からのヒント

ヒント 1:シンプルから始める

一度にすべての 9 ツールを作らないで。まずは:

  • ✅ 1 つのシンプルなツール(例:check_tdd_status
  • ✅ 基本的なエラー処理
  • ✅ 1 つのテスト

そこから反復していく。

ヒント 2:TypeScript Strict Mode を使用

{
  "compilerOptions": {
    "strict": true  # 早期にバグをキャッチ
  }
}

ヒント 3:すべてをログする

console.error('DEBUG: TDD 状態を確認', projectPath);

stderr へのログは MCP コミュニケーションを邪魔せずにデバッグを支援します。

ヒント 4:最初にローカルでテスト

# ツールロジックをテスト
node -e "import('./dist/tools/tdd.js').then(m => console.log(m.checkTDDStatus('.')))"

🎉 おめでとうございます!

完全な MCP Server をゼロから構築しました!あなたは以下を理解しました:

  • ✅ TypeScript で MCP Server を構築する方法
  • ✅ ツールの設計と実装
  • ✅ リソース管理パターン
  • ✅ テスト戦略
  • ✅ 本番対応サーバーのベストプラクティス

次は何をしましょう?

  • サーバーをコミュニティと共有
  • さらに高度な MCP 機能を探索
  • あなたの特定のワークフローのためのツールを構築

ハッピーコーディング!🚀