MCPを理解するために、MCPサーバーをNestJSで実装してみた話

最近は個人ではClaude Code/Github Copilot、会社ではGithub Copilotをがっつり使って開発しています。その中で、Model Context Protocol (MCP) の実装に興味を持って、色々と実験してみたので、その経過をまとめました

MCPは、LLMが外部のデータやツールにアクセスするための標準化されたプロトコルです。元々はAnthropic社が提唱したものでしたが、2025年12月にLinux Foundation傘下のAgentic AI Foundation (AAIF)に寄贈され、よりオープンでベンダーニュートラルなプロトコルとして発展していくことになりました。

今回は、NestJSを使ってMCPサーバーを実装し、最終的にFour Keys (DORA Metrics) の集計機能を実装してみました。

MCPで実装することの大きなメリット

従来、今回作成したような分析ツールを作る場合、以下が必要でした:

  • バックエンドAPI - データ収集・集計ロジック
  • フロントエンドUI - ダッシュボード、グラフ、チャート、テーブル等
  • データの可視化 - D3.js、Chart.js等を使った実装

しかし、MCPとして実装することで、UIの開発が不要になります:

  • LLMが自然言語インターフェースになる - 「先月のデプロイ頻度は?」と聞くだけ
  • データの解釈もLLMが支援 - 数値だけでなく、傾向分析や改善提案も自動で行われる
  • 開発コストの大幅削減 - バックエンドロジックだけに集中できる
  • 柔軟な分析が可能 - ユーザーが自由に質問を変えて、多角的に分析できる

例えば、従来のダッシュボードでは事前に決められたグラフやメトリクスしか見られませんが、MCPを通じてLLMと対話すれば、「デプロイ頻度が低い週はどんな特徴があった?」「変更失敗率が高かった時期のPRを分析して」といった柔軟な問いかけを可能にできそうです。

この「UI不要」という点は、特に社内ツールや検証的なプロジェクトにおいて非常に大きなメリットだと思います。

MCPの最新動向 - Agentic AI Foundationへの寄贈

本題に入る前に、MCPの最新動向について触れておきます。

2025年12月、MCPLinux Foundation傘下のAgentic AI Foundation (AAIF)に寄贈されました。AAIFは、Anthropic、Block、OpenAIが共同で設立した財団で、以下のプロジェクトを中心に据えています:

  • Model Context Protocol (MCP) - Anthropicから寄贈
  • goose - Blockから寄贈
  • AGENTS.md - OpenAIから寄贈

この動きの重要なポイントは:

  1. ベンダーニュートラル性の確保 - 特定企業に依存しない、オープンな標準プロトコルとして位置づけらえれ、中立的な運営体制になります。

  2. 長期的な持続可能性 - Linux Foundationの支援により、長期的な開発とメンテナンスが保証されます。

  3. エンタープライズ採用の促進 - プラチナメンバーには、AWS、Anthropic、Block、Bloomberg、Cloudflare、GoogleMicrosoft、OpenAIなどが参画しており、エンタープライズでの採用が進んでいくことが予想されます。

  4. 技術的自律性の維持 - プロトコルの技術的方向性は、メンテナーとコミュニティがSEP (Specification Enhancement Proposal) プロセスを通じて決定します。

MCPがAI エージェント時代における重要なインフラストラクチャとして位置づけられたことで、今後のエコシステムの発展が非常に楽しみです。

まずはbasic-exampleで仕様を学ぶ

MCPサーバーを実装するにあたって、まずは基本的な仕様と実装方法を理解する必要がありました。そこで、シンプルな機能だけを持つbasic-exampleブランチを作成し、MCPの基本を学ぶことにしました。

basic-exampleでは、以下のシンプルな機能を実装しています:

  1. get_current_time - 現在の日時を取得
  2. calculate - 四則演算(加算、減算、乗算、除算)
  3. save_note / get_note / list_notes - メモの保存・取得・一覧表示

この段階では、外部API連携やデータベースなどの複雑な要素は一切含めず、MCPプロトコルとNestJSの連携方法に集中しました。

プロジェクト構造

basic-exampleのプロジェクト構造は以下のようになっています:

src/
├── main.ts                          # エントリーポイント
├── app.module.ts                    # ルートモジュール
├── mcp/                             # MCPモジュール
│   ├── mcp.module.ts               # MCP設定・プロバイダー登録
│   ├── tools/                       # ツール層(MCPインターフェース)
│   │   ├── time.tool.ts            # 時刻ツール
│   │   ├── calculator.tool.ts      # 計算ツール
│   │   └── notes.tool.ts           # メモツール
│   └── services/                    # サービス層(ビジネスロジック)
│       ├── time.service.ts
│       ├── calculator.service.ts
│       └── notes.service.ts

レイヤーを分離することで、MCPプロトコルに関する部分(ツール層)とビジネスロジック(サービス層)を明確に分けています。

basic-exampleの実装 - 時刻取得を例に

それでは、実際のコードを見ながら、MCPサーバーの実装方法を解説します。最もシンプルな「現在時刻の取得」機能を例にします。

サービス層の実装

まず、サービス層で実際のビジネスロジックを実装します:

// src/mcp/services/time.service.ts
import { Injectable } from '@nestjs/common';

@Injectable()
export class TimeService {
  /**
   * 現在の日時を取得
   */
  getCurrentTime(): string {
    const now = new Date();
    return now.toLocaleString('ja-JP', { timeZone: 'Asia/Tokyo' });
  }
}

@Injectable()デコレーターを付けることで、NestJSの依存性注入システムで管理されるようになります。このサービス自体はMCPプロトコルについて何も知らず、純粋なビジネスロジックだけを持っています。

ツール層の実装

次に、MCPプロトコルとのインターフェースとなるツール層を実装します:

// src/mcp/tools/time.tool.ts
import { Injectable } from '@nestjs/common';
import { Tool } from '@rekog/mcp-nest';
import { z } from 'zod';
import { TimeService } from '../services/time.service';

@Injectable()
export class TimeTool {
  constructor(private readonly timeService: TimeService) {}

  @Tool({
    name: 'get_current_time',
    description: '現在の日時を取得します',
    parameters: z.object({}),
  })
  async getCurrentTime() {
    const time = this.timeService.getCurrentTime();
    return `現在の日時: ${time}`;
  }
}

ポイントは以下の通りです:

  1. @Toolデコレーター - このメソッドをMCPツールとして公開します
  2. name / description - LLMに対してこのツールが何をするかを説明します
  3. parameters - Zodスキーマで型安全なパラメータ定義(今回はパラメータなし)
  4. 依存性注入 - コンストラクタでTimeServiceを注入し、ビジネスロジックを呼び出します

この構造により、ビジネスロジックMCPプロトコル疎結合になり、テストや再利用が容易になります。

モジュール設定

最後に、これらをNestJSモジュールとして登録します:

// src/mcp/mcp.module.ts
import { Module } from '@nestjs/common';
import { McpModule, McpTransportType } from '@rekog/mcp-nest';
import { TimeService } from './services/time.service';
import { TimeTool } from './tools/time.tool';
// ... 他のインポート

@Module({
  imports: [
    McpModule.forRoot({
      name: 'nestjs-mcp-server',
      version: '1.0.0',
      transport: McpTransportType.STREAMABLE_HTTP,
      mcpEndpoint: '/mcp',
    }),
  ],
  providers: [
    // サービス層
    TimeService,
    // ツール層
    TimeTool,
  ],
})
export class McpToolsModule {}

McpModule.forRoot()で、MCPサーバーの基本設定を行います。今回は最新のSTREAMABLE_HTTPトランスポートを使用しています。

実際に動かしてみる

実装したMCPサーバーを実際に動かしてみましょう。

サーバーの起動

npm install
npm run build
npm start

サーバーが起動すると、以下のように表示されます:

🚀 NestJS MCP Server running on http://localhost:3000
📡 MCP Endpoint: http://localhost:3000/mcp

テストクライアントで動作確認

basic-exampleには、MCPクライアントを使ったテストスクリプトが含まれています:

node test/e2e/test-client.mjs

このスクリプトは、以下のような処理を実行します:

// 1. サーバーに接続
const client = new Client({ name: "test-client", version: "1.0.0" }, { capabilities: {} });
const transport = new StreamableHTTPClientTransport(new URL("http://localhost:3000/mcp"));
await client.connect(transport);

// 2. 利用可能なツール一覧を取得
const toolsResult = await client.listTools();

// 3. get_current_time ツールを呼び出し
const timeResult = await client.callTool({
  name: "get_current_time",
  arguments: {},
});
console.log("結果:", timeResult.content[0].text);

実行すると、以下のような出力が得られます:

MCPサーバーのテストを開始します...

サーバーに接続中...
✓ サーバーに接続しました

利用可能なツールを取得中...
✓ ツールリスト取得成功:
  - get_current_time: 現在の日時を取得します
  - calculate: 簡単な計算を実行します(加算、減算、乗算、除算)
  - save_note: キーと値のペアでメモを保存します
  - get_note: 保存されたメモを取得します
  - list_notes: 保存されているすべてのメモのキーを一覧表示します

テスト1: get_current_time を実行
✓ 結果: 現在の日時: 2025/1/12 14:30:45

テスト2: calculate (123 + 456) を実行
✓ 結果: 計算結果: 123 + 456 = 579

...

✅ すべてのテストが成功しました!

このように、MCPプロトコルを通じてツールが正しく呼び出せることが確認できます。

LLMから使う場合

Claude DesktopなどのMCPクライアントから使う場合は、設定ファイルにサーバー情報を追加します。

今回はStreamable HTTPトランスポートを使用しているため、HTTP経由で接続します:

{
  "servers": {
    "github-metrics": {
      "type": "http",
      "url": "http://localhost:3000/mcp"
    }
  }
}

サーバーを起動してから、Claudeと会話する中で自然にツールが呼び出されます:

ユーザー: 「今何時?」
Claude: (get_current_timeツールを呼び出し)
       「現在の日時は2025年1月12日 14時30分45秒です。」

なぜNestJSでMCPサーバーなのか

basic-exampleで基本を理解したところで、なぜNestJSを選んだのかについて説明します。

MCPサーバーは公式のTypeScript SDK@modelcontextprotocol/sdk)で実装できますが、今回はNestJSフレームワークと組み合わせることで、保守性と拡張性を高められる構造を持たせることにしました。@rekog/mcp-nestというライブラリが、公式SDKとNestJSの橋渡しをしてくれます。

NestJSを採用した理由は以下の通りです:

1. テストしやすく保守しやすい設計

  • 依存性注入(DI) - コンストラクタインジェクションで疎結合な設計を実現。モックに置き換えやすく、ユニットテストが書きやすい
  • レイヤー分離 - ツール層(MCPインターフェース)とサービス層(ビジネスロジック)を明確に分離。MCPプロトコルの変更がビジネスロジックに影響しない
  • モジュールシステム - 機能ごとにモジュール化することで、コードの見通しが良くなり、変更の影響範囲を限定できる

2. 将来の機能追加がしやすい

basic-exampleで基本を理解した後、Four Keysのような複雑な機能を追加する際に、NestJSのエコシステムを活用できます:

  • データベース統合 - TypeORM、Prismaなどを標準的な方法で追加できる
  • 外部API連携 - HttpModuleを使った統一的な実装パターン
  • 認証・認可 - Guardやミドルウェアを使った標準的な実装
  • 横断的関心事 - キャッシング、ロギング、エラーハンドリングをインターセプターで実装

3. ビジネスロジックの再利用性

ツール層とサービス層を分離することで、サービス層は他の用途でも使えます:

  • REST APIとしても公開可能 - 同じサービス層を使ってHTTP APIを提供できる
  • CLI ツールでも利用可能 - コマンドラインツールからも同じロジックを呼び出せる
  • テストの容易さ - 各層を独立してテストでき、テストコードの保守性も向上

Four Keysの実装へ

basic-exampleでMCPの基本を理解した後、より実践的な題材としてFour Keys (DORA Metrics) の計測機能を実装しました(mainブランチ)。

Four Keysは、DevOpsのパフォーマンスを測定するための4つの主要指標です:

  1. デプロイ頻度 (Deployment Frequency) - 本番環境へのデプロイ頻度
  2. リードタイム (Lead Time for Changes) - コード変更から本番反映までの時間
  3. 変更失敗率 (Change Failure Rate) - デプロイ後に障害が発生した割合
  4. 平均復旧時間 (MTTR) - 障害発生から復旧までの平均時間

アーキテクチャの拡張

Four Keys実装では、basic-exampleの構造を拡張し、以下のような階層構造にしました:

src/mcp/
├── tools/four-keys/           # Four Keysツール層
│   ├── deployment-frequency.tool.ts
│   ├── lead-time.tool.ts
│   ├── change-failure-rate.tool.ts
│   ├── mttr.tool.ts
│   └── summary.tool.ts        # 4つのメトリクスをまとめて取得
├── services/
│   ├── github/                # GitHub統合サービス
│   │   ├── github-auth.service.ts    # GitHub App認証
│   │   └── github-api.service.ts     # GitHub APIクライアント
│   └── four-keys/             # Four Keys計算サービス
│       ├── deployment-frequency.service.ts
│       ├── lead-time.service.ts
│       ├── change-failure-rate.service.ts
│       └── mttr.service.ts
└── types/                     # 型定義
    ├── github.types.ts
    └── four-keys.types.ts

実装の特徴

GitHub API連携

Four Keysの計測には、GitHub APIからのデータ取得が必要です。GitHub App認証を実装し、以下の情報を収集します:

  • リリース、タグ、GitHub Actionsの実行履歴(デプロイ頻度)
  • プルリクエストの作成〜マージ時間(リードタイム)
  • ホットフィックスPR、インシデントIssue(変更失敗率、MTTR

柔軟なデプロイ検出

チームによってデプロイの運用方法は異なるため、3つの検出方法をサポート:

  • GitHub Releases - リリースベースのデプロイ
  • Git Tags - タグベースのデプロイ(正規表現フィルタリング可能)
  • GitHub Actions Workflow - 特定のワークフロー実行履歴

DORAレベルの自動評価

各メトリクスについて、DORAが定義するパフォーマンスレベル(Elite/High/Medium/Low)を自動評価します。例えば、デプロイ頻度の場合:

  • Elite: 1日に複数回
  • High: 週に1回〜月に1回
  • Medium: 月に1回〜半年に1回
  • Low: 半年に1回未満

使用例

LLMと会話しながら、チームのDevOpsパフォーマンスを分析できます:

ユーザー: 「my-org/my-repoリポジトリの過去1ヶ月のFour Keysメトリクスを教えて」

Claude: (get_four_keys_summaryツールを呼び出し)

「過去1ヶ月のFour Keysメトリクスは以下の通りです:

デプロイ頻度: 15回/月 (0.5回/日) - Performance Level: High
リードタイム: 平均 12.5時間 - Performance Level: Elite
変更失敗率: 6.7% - Performance Level: Elite
MTTR: 平均 2.3時間 - Performance Level: Elite

総合評価: Elite

デプロイ頻度をもう少し上げることで、Eliteレベルに到達できそうです。」

テスト戦略

Four Keys実装では、95%以上のテストカバレッジを達成しています。Vitestを使用し、GitHub APIはモックで置き換えることで、高速で信頼性の高いテストを実現しています。

GitHub Actionsでは、テスト実行とカバレッジレポートの自動投稿を設定しており、継続的に品質を担保しています。

学んだこと・工夫したこと

1. 段階的な学習アプローチ

basic-exampleで基本を理解してから、Four Keysという実践的な題材に進むという段階的なアプローチは非常に効果的でした。MCPの仕様を理解してから、実際のビジネスロジックの実装に集中できました。

2. レイヤー分離の重要性

ツール層とサービス層を明確に分離したことで:

特に、Four Keys実装では外部API連携やデータ処理などが絡むため、この分離が非常に役立ちました。

3. ブランチ戦略

学習曲線を考慮した2つのブランチ構成:

  • basic-exampleブランチ: MCPの基本を学ぶ
  • mainブランチ: 実践的な実装を学ぶ

これにより、初めてMCPを触る人でも段階的に学べるリポジトリになりました。

4. 型安全性の追求

Zodによるパラメータバリデーションと、TypeScriptの型定義を組み合わせることで、実行時と開発時の両方で型安全性を確保できました。

今後の展望

現在の実装は試験実装のためMVPレベルですが、今後以下のような拡張を考えています:

  1. データベース統合

    • 履歴データの保存とトレンド分析
    • ダッシュボード機能
  2. カスタマイズ性の向上

    • チーム固有のルール設定
    • Slack/Teams連携
  3. 認証・認可

    • マルチテナント対応
    • ロールベースのアクセス制御

NestJSの構造により、これらの拡張も体系的に進められると考えています。

おわりに

NestJSでMCPサーバーを実装することで、保守性と拡張性の高いツールの基礎を作ることができました。

特に、basic-exampleで基本を学び、Four Keysという実践的な題材で応用するという段階的なアプローチは、MCPの理解を深める上で非常に効果的でした。レイヤー分離や依存性注入といった設計により、テストもしやすく、将来の機能追加にも対応しやすい構造になっています。

また、MCPとLLMを組み合わせることで、「チームのデプロイ頻度は?」「先月のリードタイムは?」といった質問に自然言語で答えてもらえるのは非常に便利です。DevOpsメトリクスの可視化と分析が、より身近になると感じました。

MCPがAgentic AI Foundationに寄贈されたことで、今後さらにエコシステムが発展していくことが期待されます。このような標準プロトコルを使った開発が、より一般的になっていくと思います。

コードは以下のリポジトリで公開していますので、ぜひ試してみてください!

https://github.com/kaz29/mcp-server-example https://github.com/kaz29/mcp-server-example/tree/basic-example

参考リンク