DEV Community

Cover image for MCPサーバーテストプレイブック:Apidogを活用した手動・自動テスト
Akira
Akira

Posted on • Originally published at apidog.com

MCPサーバーテストプレイブック:Apidogを活用した手動・自動テスト

「Ableton Live MCP」のShow HN投稿が今週初め、118ポイントと78件のコメントを獲得しました。Model Context Protocol(MCP)は、Anthropic専用の実験から、1年足らずでエージェント統合の事実上のレイヤーへ広がりました。今では、Claude Desktop、Cursor、独自エージェントから外部ツール・リソース・プロンプトを呼び出すための実装対象として扱われています。

今すぐApidogを試す

一方で、MCPサーバーのテスト方法はまだ整っていません。stdio経由でJSON-RPCを手動実行する方法は「hello world」には十分ですが、ツールが12個、プロンプトが3個、外部API依存があるサーバーではすぐに破綻します。

この記事では、MCPサーバーを手動で検証し、その後 Apidog で自動テスト化する手順をまとめます。目的は、MCPサーバーを通常のAPIと同じように、契約、モック、回帰テスト付きで出荷できる状態にすることです。

より広いエージェント設計の文脈から来ている場合は、agents.mdガイド も合わせて読むと、MCPサーバーの契約をチームで共有しやすくなります。

TL;DR

  • MCP はAnthropicのModel Context Protocolであり、stdioまたはHTTP上のJSON-RPC 2.0として動作します。
  • 主なプリミティブは、ツール、リソース、プロンプトです。
  • MCPサーバーのテストでは、主に initializetools/listtools/callresources/readprompts/get の応答を検証します。
  • 最初はMCP Inspectorやstdioで手動確認し、ワイヤーレベルのリクエスト・レスポンスを把握します。
  • 次に Apidog にJSON-RPCリクエストを保存し、JSONPathアサーション、モック、CI実行を追加します。
  • 外部API依存はApidogのモックサーバーで置き換え、CIを高速かつ決定論的にします。
  • リクエストコレクション、モック、CIランナーをまとめて使う場合は、Apidogをダウンロード してください。

MCPの正体を短く整理する

Model Context Protocol仕様 は、AIクライアントと外部機能をつなぐJSON-RPC 2.0ベースのプロトコルです。

クライアントには、たとえば次のようなものがあります。

  • Claude Desktop
  • Cursor
  • 独自のAIエージェント
  • MCP対応IDEや開発ツール

MCPクライアントはサーバーを起動し、最初に initialize ハンドシェイクを実行します。その後、ツール、リソース、プロンプトに対してJSON-RPC呼び出しを行います。

テストで特に重要なのは次の呼び出しです。

メソッド 目的
initialize プロトコルバージョンと機能のネゴシエーション
tools/list 公開ツールと入力スキーマの一覧取得
tools/call ツールの実行
resources/list 読み取り可能なリソース一覧の取得
resources/read URI指定リソースの読み取り
prompts/list プロンプトテンプレート一覧の取得
prompts/get プロンプトテンプレートのレンダリング

トランスポートは主に2種類です。

  • stdio:stdin/stdoutで改行区切りのJSON-RPCフレームをやり取りする
  • HTTP:通常は POST / とSSEを使ってストリーミングする

ローカルMCPサーバーはstdio、リモートMCPサーバーはHTTPを使うことが多いです。

MCPでは、tools/list のレスポンス形状が壊れるだけで、Claude DesktopやCursorなど複数のクライアントが同時にツールを認識できなくなります。したがって、MCPサーバーは通常のAPIと同じく契約テストが必要です。

MCPサーバーでテストすべき項目

堅牢なMCPサーバーテストでは、少なくとも次の6領域を確認します。

1. プロトコル準拠

initialize が正しい protocolVersion を返すか確認します。

例:

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "protocolVersion": "2026-04-01",
    "capabilities": {
      "tools": {},
      "resources": {},
      "prompts": {}
    },
    "serverInfo": {
      "name": "example-mcp-server",
      "version": "1.0.0"
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

確認ポイント:

  • protocolVersion が期待値と一致する
  • 実際に対応している機能だけを capabilities に出している
  • serverInfo.nameserverInfo.version が存在する

2. スキーマの正確性

tools/list 内の各ツールに、正しい inputSchema があるか確認します。

見るべき項目:

  • name が一意である
  • description が空ではない
  • inputSchema が有効なJSON Schemaである
  • 必須引数が required に入っている
  • 型が実装と一致している

悪い例:

{
  "name": "get_weather",
  "description": "",
  "inputSchema": {
    "type": "object",
    "properties": {
      "city": {}
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

改善例:

{
  "name": "get_weather",
  "description": "指定した都市の現在の天気を取得します。",
  "inputSchema": {
    "type": "object",
    "properties": {
      "city": {
        "type": "string",
        "description": "天気を取得する都市名。例: Tokyo"
      }
    },
    "required": ["city"]
  }
}
Enter fullscreen mode Exit fullscreen mode

3. ツールの動作

tools/call が期待通りのコンテンツブロックを返すか確認します。

成功時の例:

{
  "jsonrpc": "2.0",
  "id": 42,
  "result": {
    "content": [
      {
        "type": "text",
        "text": "Tokyo: 24°C, cloudy"
      }
    ]
  }
}
Enter fullscreen mode Exit fullscreen mode

確認ポイント:

  • content が配列である
  • 各ブロックに type がある
  • textimageresource などの型が仕様に沿っている
  • 成功時に isError: true が付いていない

4. ツールのエラー処理

MCPでは、ツール実行時の失敗はJSON-RPCエラーではなく、通常の結果として返します。

推奨されるエラー例:

{
  "jsonrpc": "2.0",
  "id": 43,
  "result": {
    "isError": true,
    "content": [
      {
        "type": "text",
        "text": "city is required"
      }
    ]
  }
}
Enter fullscreen mode Exit fullscreen mode

避けるべき例:

{
  "jsonrpc": "2.0",
  "id": 43,
  "error": {
    "code": -32602,
    "message": "city is required"
  }
}
Enter fullscreen mode Exit fullscreen mode

JSON-RPCエラーは、プロトコルレベルの失敗を示します。ツール内部の入力エラーや外部APIエラーに使うと、クライアントが接続を閉じる場合があります。

5. リソースアクセス

resources/list で返したURIが、resources/read で実際に読めるか確認します。

確認ポイント:

  • URIが安定している
  • resources/read が同じURIを解決できる
  • ページネーションがある場合、2ページ目以降も読める
  • 存在しないURIに対して適切なエラーを返す

6. プロンプトのレンダリング

prompts/get が正しい messages 配列を返すか確認します。

確認ポイント:

  • messages が配列である
  • 各メッセージに rolecontent がある
  • 引数が正しい位置に展開される
  • 必須引数が不足した場合にエラーになる

stdioでMCPサーバーを手動テストする

まずは自動化せず、MCPサーバーがワイヤーレベルで何を返すか確認します。

まだサーバーを作っていない場合は、PythonまたはTypeScriptで 公式MCP SDKクイックスタート を使って最小構成を作成します。

MCP Inspectorで起動確認する

npx @modelcontextprotocol/inspector node your-server.js
Enter fullscreen mode Exit fullscreen mode

MCP Inspectorは、MCPサーバーと通信するローカルWeb UIを起動します。

ここで確認すること:

  • サーバーが起動する
  • initialize が成功する
  • tools/list が表示される
  • 各ツールを手動実行できる
  • エラー時のレスポンス形状が正しい

生のstdioでJSON-RPCを送る

Inspectorで問題がなければ、次は生のJSON-RPCを送ります。

echo '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2026-04-01","capabilities":{}}}' | node your-server.js
Enter fullscreen mode Exit fullscreen mode

次に、tools/list を送ります。

echo '{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}' | node your-server.js
Enter fullscreen mode Exit fullscreen mode

ツール呼び出しの例:

echo '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"get_weather","arguments":{"city":"Tokyo"}}}' | node your-server.js
Enter fullscreen mode Exit fullscreen mode

この段階で、次のリクエスト・レスポンスを保存しておきます。

  • initialize
  • tools/list
  • 主要ツールの正常系 tools/call
  • 主要ツールの異常系 tools/call
  • resources/list
  • resources/read
  • prompts/list
  • prompts/get

これらが、後で Apidog に登録する契約テストの元になります。

手動テストからApidogでの自動化へ移行する

手動テストは、初期の形状バグを見つけるには有効です。しかし、次のような状態になったら自動化が必要です。

  • ツール数が増えてきた
  • 外部API依存がある
  • PRごとに回帰確認したい
  • tools/list のスキーマ変更を検知したい
  • Claude Desktopで毎回手動確認するのが遅い

基本パターンは次の通りです。

  1. 手動で取得したJSON-RPCリクエストをApidogに保存する
  2. 各レスポンスにJSONPathアサーションを追加する
  3. 外部APIをモックする
  4. CIでスイートを実行する

1. MCPサーバー用のApidogプロジェクトを作成する

Apidogで新しいプロジェクトを作成します。

HTTP MCPサーバーの場合は、ベースURLにMCPサーバーのHTTPエンドポイントを設定します。

例:

http://localhost:3000
Enter fullscreen mode Exit fullscreen mode

stdioのみのMCPサーバーの場合は、テスト用に薄いHTTPラッパーを用意します。公式Inspectorを使うか、HTTPで受け取ったJSON-RPCをstdioに転送する小さなNode.jsスクリプトを使います。

たとえば、テスト用ラッパーの構成は次のようになります。

Apidog
  -> HTTP POST /rpc
    -> wrapper.js
      -> stdio
        -> your-server.js
Enter fullscreen mode Exit fullscreen mode

非HTTPバックエンドをHTTP経由でテストする考え方は、2026年のPostmanなしのAPIテスト でも同じです。

2. 標準リクエストを保存する

Apidogに、MCPの主要メソッドをリクエストとして保存します。

最低限、次を登録します。

  • initialize
  • tools/list
  • tools/call 正常系
  • tools/call 異常系
  • resources/list
  • resources/read
  • prompts/list
  • prompts/get

tools/call のリクエストボディ例:

{
  "jsonrpc": "2.0",
  "id": 42,
  "method": "tools/call",
  "params": {
    "name": "get_weather",
    "arguments": {
      "city": "Tokyo"
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

異常系も必ず保存します。

{
  "jsonrpc": "2.0",
  "id": 43,
  "method": "tools/call",
  "params": {
    "name": "get_weather",
    "arguments": {}
  }
}
Enter fullscreen mode Exit fullscreen mode

3. JSONPathアサーションを追加する

自動テストで重要なのは、リクエストを送ることではなく、レスポンスの契約を検証することです。

initialize のアサーション例

確認項目:

  • $.result.protocolVersion が期待値と一致する
  • $.result.capabilities が存在する
  • $.result.serverInfo.name が存在する

例:

$.result.protocolVersion == "2026-04-01"
$.result.capabilities exists
$.result.serverInfo.name exists
Enter fullscreen mode Exit fullscreen mode

tools/list のアサーション例

確認項目:

  • $.result.tools が存在する
  • $.result.tools が空でない
  • 各ツールに name がある
  • 各ツールに description がある
  • 各ツールに inputSchema がある

例:

$.result.tools exists
$.result.tools.length > 0
$.result.tools[0].name exists
$.result.tools[0].description exists
$.result.tools[0].inputSchema exists
Enter fullscreen mode Exit fullscreen mode

ツール数が固定されている場合は、件数も検証できます。

$.result.tools.length == 10
Enter fullscreen mode Exit fullscreen mode

tools/call 正常系のアサーション例

確認項目:

  • $.result.content が配列である
  • $.result.content[0].type が期待値と一致する
  • $.result.isError が存在しない、または false

例:

$.result.content exists
$.result.content[0].type == "text"
$.result.isError != true
Enter fullscreen mode Exit fullscreen mode

tools/call 異常系のアサーション例

確認項目:

  • $.result.isErrortrue
  • エラーメッセージが存在する
  • JSON-RPCの error ではなく result として返る

例:

$.result.isError == true
$.result.content[0].text exists
$.error not exists
Enter fullscreen mode Exit fullscreen mode

エラーメッセージの完全一致は避け、安定したエラーコードや正規表現で検証する方が安全です。

4. アップストリームAPIをモックする

多くのMCPサーバーは、外部APIをラップします。

例:

  • 天気API
  • GitHub
  • Linear
  • Notion
  • 社内データベース
  • インシデント管理API

CIで毎回ライブAPIを呼ぶと、次の問題が起きます。

  • レート制限に引っかかる
  • ネットワーク障害でテストが落ちる
  • テスト結果がデータ状態に依存する
  • 外部API利用コストが増える

この部分はApidogのモックサーバーで置き換えます。

手順:

  1. Apidogで外部APIのエンドポイントを定義する
  2. レスポンス例を登録する
  3. モックサーバーを起動する
  4. テスト時だけMCPサーバーの外部API URLをモックURLに向ける
  5. 本番実行時は実APIのURLに戻す

設定例:

# CI
WEATHER_API_BASE_URL=https://mock.apidog.com/project/xxx

# production
WEATHER_API_BASE_URL=https://api.weather.example.com
Enter fullscreen mode Exit fullscreen mode

モックを使うことで、MCPサーバーのテストは次のようになります。

  • 外部ネットワークに依存しない
  • 数秒で終わる
  • 毎回同じ結果になる
  • スキーマ回帰を早期に検知できる

モックを使った契約優先のワークフローは、契約優先のAPI開発 でも詳しく説明しています。

5. CIでMCPテストスイートを実行する

ApidogプロジェクトはCLIランナーで実行できます。apidog run は保存済みリクエストを実行し、アサーションを評価し、失敗時に非ゼロで終了します。

GitHub Actionsの最小構成例:

name: MCP server tests

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4
        with:
          node-version: 22

      - run: npm ci

      - name: Start MCP HTTP wrapper
        run: node test/wrapper.js &

      - name: Run Apidog suite
        run: npx apidog run --project-id $APIDOG_PROJECT --env ci
        env:
          APIDOG_PROJECT: ${{ secrets.APIDOG_PROJECT }}
          APIDOG_TOKEN: ${{ secrets.APIDOG_TOKEN }}
Enter fullscreen mode Exit fullscreen mode

これで、すべてのpushとpull requestでMCP契約テストが実行されます。

tools/list のスキーマ変更、必須引数の欠落、ツールのエラー形式の崩れは、リリース前に検知できます。

良いMCPテストカバレッジの目安

Apidog でMCPサーバーをテストする場合、現実的なスイートは次の構成になります。

  • initialize:1件
  • tools/list:1件
  • 各ツールの tools/call:2〜4件
    • 正常系
    • 必須引数不足
    • 型不正
    • 外部APIエラー
  • 各リソースファミリーの resources/list:1件
  • 各リソースファミリーの resources/read:1件
  • 各プロンプトの prompts/list:1件
  • 各プロンプトの prompts/get:1件

たとえば、次のMCPサーバーを考えます。

  • ツール:10個
  • リソース:3種類
  • プロンプト:4個

この場合、テストスイートはおおよそ50〜70リクエストになります。モックサーバーを使えば、ローカルでもCIでも短時間で実行できます。

MCPサーバーテストでよくある間違い

initialize を省略する

一部のMCPサーバーは、initialize のタイミングでツールレジストリや設定を初期化します。

そのため、いきなり tools/list を呼ぶと落ちる場合があります。

必ず次の順序でテストします。

  1. initialize
  2. tools/list
  3. tools/call
  4. resources/*
  5. prompts/*

エラーメッセージを完全一致で検証する

エラーテキストは変更されやすいため、完全一致は壊れやすいです。

避けたい例:

$.result.content[0].text == "city is required"
Enter fullscreen mode Exit fullscreen mode

より安定した例:

$.result.isError == true
$.result.content[0].text contains "city"
Enter fullscreen mode Exit fullscreen mode

可能であれば、ツール側で安定したエラーコードを含める設計にします。

モックと本番レスポンスが乖離する

モックが実APIと違う形状を返していると、テストは成功しても本番で壊れます。

対策:

  • リリースごとに実APIレスポンスからモックを更新する
  • 重要な外部APIは契約テストを別途持つ
  • モックフィクスチャをレビュー対象にする

ストリーミングをテストしない

HTTP MCPサーバーはSSEでツール結果をストリーミングすることがあります。

確認ポイント:

  • SSEを有効にしたリクエストでテストする
  • 組み立て後の結果に対してアサーションする
  • タイムアウトや途中切断も確認する

Apidogは保存済みリクエストでSSEを扱えます。HTTP MCPサーバーをテストする場合は、リクエスト設定でストリーミングを有効にしてください。

並行実行をテストしない

MCPクライアントは、エージェントループ内で複数の tools/call を並行実行することがあります。

単一リクエストでは成功しても、共有状態やキャッシュが原因で本番だけ壊れるケースがあります。

確認すべきこと:

  • 同じツールを同時に複数回呼ぶ
  • 異なるツールを同時に呼ぶ
  • 外部APIモックに遅延を入れる
  • タイムアウト時の挙動を見る

プロトコルエラーとツールエラーを混同する

MCPでは、プロトコルレベルの失敗とツール実行の失敗を分けます。

  • JSON-RPCエラー:不正なメソッド、無効なJSON-RPC、プロトコル違反
  • isError: true:ツール実行時の入力エラー、外部APIエラー、業務エラー

これを混同すると、Claude Desktopなどのクライアントが接続を閉じる場合があります。同様の契約バグについては、APIプラットフォームの契約優先開発 でも扱っています。

実世界のユースケース

社内インシデント管理MCPサーバー

あるチームは、社内インシデント管理API用のMCPサーバーを構築しました。

Apidogで tools/list の形状にアサーションを追加したところ、1週間で3件の回帰を検出しました。いずれも、Claude Desktopを使うエンジニア全員に壊れたツール定義が配布される可能性がある変更でした。

Notion用オープンソースMCPサーバー

Notion向けMCPサーバーを公開している開発者は、Apidogモックを使ってCI中のNotion API呼び出しを置き換えています。

結果:

  • Notionのレート制限に依存しない
  • すべてのPRでテストが走る
  • コントリビューターがNotion APIキーを持つ必要がない
  • テストが数秒で完了する

複数MCPサーバーを運用するプラットフォームチーム

14個の内部MCPサーバーを運用するチームでは、各サーバーの契約を共有Apidogワークスペースに置いています。

新しいMCPサーバーはベーステストスイートを継承し、レビュー担当者はマージ前にスキーマ差分を確認できます。

特に tools/list のアサーションにより、引数名変更による大規模なクライアント影響を事前に検知できました。

実装チェックリスト

MCPサーバーのテストを始めるなら、次の順で進めるのが実用的です。

  1. MCP Inspectorでサーバーを起動する
  2. initialize が成功することを確認する
  3. tools/list のレスポンスを保存する
  4. 各ツールの正常系 tools/call を実行する
  5. 各ツールの異常系 tools/call を実行する
  6. resources/*prompts/* を確認する
  7. ApidogにJSON-RPCリクエストを登録する
  8. JSONPathアサーションを追加する
  9. 外部APIをApidogモックに置き換える
  10. CIで apidog run を実行する

結論

MCPは急速に普及しましたが、テスト方法はまだ手動中心です。しかし、MCPサーバーはJSON-RPC APIです。REST APIと同じように、契約、モック、CIによる回帰テストを持つべきです。

要点は次の5つです。

  • MCPサーバーはJSON-RPC APIとして扱う
  • 最初はMCP Inspectorとstdioで手動確認する
  • 手動で得たリクエストを Apidog に保存する
  • initializetools/listtools/callresources/readprompts/get にアサーションを追加する
  • 外部APIは Apidog のモックで置き換え、CIを安定させる

次のステップはシンプルです。Apidog でプロジェクトを作成し、手動でキャプチャした tools/list のリクエストを貼り付け、JSONPathアサーションを1つ追加してください。そこから、MCPサーバーの契約テストを段階的に拡張できます。

よくある質問

MCPとは何ですか?

MCP(Model Context Protocol)は、AIクライアントが外部ツール、リソース、プロンプトを呼び出すためのAnthropicのオープン仕様です。stdioまたはストリーミング可能なHTTP上でJSON-RPC 2.0として動作します。

MCPの完全な仕様modelcontextprotocol.io で公開されています。

HTTPラッパーなしでMCPサーバーをテストできますか?

手動テストなら可能です。公式MCPインスペクター はstdioと直接通信できます。

ただし、Apidog で自動テストする場合は、CI中だけstdioを薄いHTTPラッパーで包む構成が実用的です。本番トラフィックは引き続きstdioのままで問題ありません。

MCPサーバーが呼び出す外部APIはどうモックしますか?

Apidogプロジェクトで外部APIエンドポイントをモックとして定義します。テスト中はMCPサーバーの設定をモックURLに向け、本番では実URLに切り替えます。

同じパターンは QAエンジニア向けのAPIテストツール でも説明しています。

ツール結果のストリーミングはどうテストしますか?

HTTP MCPサーバーはSSEでツール結果をストリーミングすることがあります。Apidogの保存済みリクエストでSSEを有効にし、組み立て後の結果に対してアサーションを実行します。

プロトコルバージョンはテストすべきですか?

はい。initializeprotocolVersion は必ず固定してアサーションしてください。ここがずれると、クライアントとの互換性問題がサイレントに発生します。

実際のClaude Desktopに対してテストすべきですか?

はい。リリース前のスモークテストとしては有効です。

ただし、Claude Desktopを回帰テストの中心に置くべきではありません。手動で遅く、非決定論的だからです。回帰テストは Apidog で自動化し、Claude Desktopは最終確認に使うのが実用的です。

実際のMCPサーバー実装はどこで見られますか?

公式MCPサーバーリポジトリ に、ファイルシステム、GitHub、Slack、Postgresなどのリファレンス実装があります。ツール定義、入力スキーマ、エラー処理を見ると、良いMCPサーバーの形状を理解しやすくなります。

Top comments (0)