要点
TypeScriptでMCPサーバーを構築し、`run_test`、`validate_schema`、`list_environments`の3つのツールを公開します。Claude Codeの場合は`~/.claude/settings.json`、Cursorの場合は`.cursor/mcp.json`で設定します。これにより、AIエージェントはチャットインターフェースを離れることなく、Apidogテストの実行、OpenAPIスキーマの検証、環境の取得が可能になります。全ソースコードは約150行で、`@modelcontextprotocol/sdk`パッケージを使用しています。
Claude Code、Cursor、およびその他のAIエージェントが、チャットインターフェースを離れることなく、Apidog APIテストの実行、スキーマの検証、応答の比較を行えるMCPサーバーを構築します。
これこそが、モデルコンテキストプロトコル(MCP)が実現することです。MCPは、AIエージェントが標準化されたインターフェースを介して外部ツールにアクセスすることを可能にします。Apidog用のMCPサーバーを構築すれば、AIエージェントはコンテキストを切り替えることなく、テストの実行、スキーマの検証、環境の取得を行うことができます。
MCPとは何か?
MCP(モデルコンテキストプロトコル)は、AIエージェントが外部ツールやデータソースにアクセスするためのプロトコルです。Claude Code、Cursor、およびその他のMCP互換クライアント間で動作するプラグインシステムと考えることができます。
MCPサーバーは、ツール(エージェントが呼び出せる関数)とリソース(エージェントが読み取れるデータ)を公開します。あなたのApidog MCPサーバーは、APIテスト用のツールを公開することになります。
┌─────────────────┐ ┌──────────────────┐ ┌─────────────┐
│ AI Agent │ │ MCP Server │ │ Apidog │
│ (Claude Code) │◄───────►│ (Your Code) │◄───────►│ API │
└─────────────────┘ JSON └──────────────────┘ HTTP └─────────────┘
ステップ1: プロジェクトのセットアップ
新しいTypeScriptプロジェクトを作成します:
mkdir apidog-mcp-server
cd apidog-mcp-server
npm init -y
npm install @modelcontextprotocol/sdk zod
npm install -D typescript @types/node
tsconfig.jsonを作成します:
{
"compilerOptions": {
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
},
"include": ["src/**/*"],
"exclude": ["node_modules"]
}
package.jsonにビルドスクリプトを追加します:
{
"scripts": {
"build": "tsc",
"start": "node dist/index.js"
}
}
ステップ2: MCPサーバーのスケルトンを作成する
src/index.tsを作成します:
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
const server = new McpServer({
name: "apidog",
version: "1.0.0",
description: "Apidog API testing tools for AI agents"
});
// Tools will be defined here
const transport = new StdioServerTransport();
await server.connect(transport);
このスケルトンは、MCPサーバーを作成し、それを標準入出力(stdio)トランスポートに接続します。このトランスポートは、AIエージェントとサーバー間の通信を標準入出力経由で処理します。
ステップ3: run_testツールの定義
最初のツールをsrc/index.tsに追加します:
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
const server = new McpServer({
name: "apidog",
version: "1.0.0",
description: "Apidog API testing tools for AI agents"
});
// Tool: run_test
server.tool(
"run_test",
{
projectId: z.string().describe("Apidog project ID (found in project URL)"),
environmentId: z.string().optional().describe("Optional environment ID for test execution"),
testSuiteId: z.string().optional().describe("Optional test suite ID to run specific suite")
},
async ({ projectId, environmentId, testSuiteId }) => {
const apiKey = process.env.APIDOG_API_KEY;
if (!apiKey) {
return {
content: [{
type: "text",
text: "Error: APIDOG_API_KEY environment variable not set"
}]
};
}
// Build API URL
let url = `https://api.apidog.com/v1/projects/${projectId}/tests/run`;
const params = new URLSearchParams();
if (environmentId) params.append("environmentId", environmentId);
if (testSuiteId) params.append("testSuiteId", testSuiteId);
if (params.toString()) url += `?${params.toString()}`;
try {
const response = await fetch(url, {
method: "POST",
headers: {
"Authorization": `Bearer ${apiKey}`,
"Content-Type": "application/json"
}
});
if (!response.ok) {
const error = await response.text();
return {
content: [{
type: "text",
text: `API Error: ${response.status} ${error}`
}]
};
}
const results = await response.json();
return {
content: [{
type: "text",
text: JSON.stringify(results, null, 2)
}]
};
} catch (error) {
return {
content: [{
type: "text",
text: `Request failed: ${error instanceof Error ? error.message : String(error)}`
}]
};
}
}
);
const transport = new StdioServerTransport();
await server.connect(transport);
ツールの定義は3つの部分から構成されます:
- 名前 —
run_test(エージェントは名前でツールを選択するため、分かりやすく記述する) - スキーマ — パラメータの説明付きZod検証
- ハンドラー — Apidog APIを呼び出す非同期関数
ステップ4: validate_schemaツールの追加
デプロイ前にOpenAPIエラーを捕捉するために、スキーマ検証を追加します:
// Tool: validate_schema
server.tool(
"validate_schema",
{
schema: z.object({}).describe("OpenAPI 3.x schema object to validate"),
strict: z.boolean().optional().default(false).describe("Enable strict mode for additional checks")
},
async ({ schema, strict }) => {
const apiKey = process.env.APIDOG_API_KEY;
if (!apiKey) {
return {
content: [{
type: "text",
text: "Error: APIDOG_API_KEY environment variable not set"
}]
};
}
try {
const response = await fetch("https://api.apidog.com/v1/schemas/validate", {
method: "POST",
headers: {
"Authorization": `Bearer ${apiKey}`,
"Content-Type": "application/json"
},
body: JSON.stringify({ schema, strict })
});
const result = await response.json();
if (!response.ok) {
return {
content: [{
type: "text",
text: `Validation failed: ${JSON.stringify(result.errors, null, 2)}`
}]
};
}
return {
content: [{
type: "text",
text: result.valid
? "Schema is valid OpenAPI 3.x"
: `Warnings: ${JSON.stringify(result.warnings, null, 2)}`
}]
};
} catch (error) {
return {
content: [{
type: "text",
text: `Validation failed: ${error instanceof Error ? error.message : String(error)}`
}]
};
}
}
);
ステップ5: list_environmentsツールの追加
利用可能なテスト環境を取得するツールを追加します:
// Tool: list_environments
server.tool(
"list_environments",
{
projectId: z.string().describe("Apidog project ID")
},
async ({ projectId }) => {
const apiKey = process.env.APIDOG_API_KEY;
if (!apiKey) {
return {
content: [{
type: "text",
text: "Error: APIDOG_API_KEY environment variable not set"
}]
};
}
try {
const response = await fetch(
`https://api.apidog.com/v1/projects/${projectId}/environments`,
{
headers: {
"Authorization": `Bearer ${apiKey}`
}
}
);
if (!response.ok) {
const error = await response.text();
return {
content: [{
type: "text",
text: `API Error: ${response.status} ${error}`
}]
};
}
const environments = await response.json();
return {
content: [{
type: "text",
text: environments.length === 0
? "No environments found for this project"
: environments.map((e: any) =>
`- ${e.name} (ID: ${e.id})${e.isDefault ? " [default]" : ""}`
).join("\n")
}]
};
} catch (error) {
return {
content: [{
type: "text",
text: `Request failed: ${error instanceof Error ? error.message : String(error)}`
}]
};
}
}
);
ステップ6: ビルドとテスト
サーバーをビルドします:
npm run build
シンプルなMCPクライアントでテストします。test-client.jsを作成します:
import { spawn } from "child_process";
const server = spawn("node", ["dist/index.js"], {
env: { ...process.env, APIDOG_API_KEY: "your-api-key" }
});
server.stdout.on("data", (data) => {
console.log(`Server output: ${data}`);
});
server.stderr.on("data", (data) => {
console.error(`Server error: ${data}`);
});
// Send a test message
const message = {
jsonrpc: "2.0",
id: 1,
method: "initialize",
params: {
protocolVersion: "2024-11-05",
capabilities: {},
clientInfo: { name: "test-client", version: "1.0.0" }
}
};
server.stdin.write(JSON.stringify(message) + "\n");
ステップ7: Claude Codeの構成
Claude Codeの設定にMCPサーバーを追加します:
~/.claude/settings.jsonを作成または編集します:
{
"mcpServers": {
"apidog": {
"command": "node",
"args": ["/absolute/path/to/apidog-mcp-server/dist/index.js"],
"env": {
"APIDOG_API_KEY": "your-api-key-here"
}
}
}
}
Claude Codeを再起動します。APIテストのヘルプを求めると、Apidogツールが表示されるはずです。
Claude Codeでの使用法:
Use the run_test tool to run tests on my Apidog project.
Project ID: proj_12345
Environment: staging
Validate this OpenAPI schema against Apidog rules:
[paste schema]
List all environments for project proj_12345
ステップ8: Cursorの構成
Cursorは同様のMCP設定を使用します。プロジェクト内に.cursor/mcp.jsonを作成します:
{
"mcpServers": {
"apidog": {
"command": "node",
"args": ["/absolute/path/to/apidog-mcp-server/dist/index.js"],
"env": {
"APIDOG_API_KEY": "your-api-key-here"
}
}
}
}
Cursorでの使用法:
@apidog run_test projectId="proj_12345" environmentId="staging"
完全なソースコード
以下に、src/index.tsの完全なコードを示します:
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
const server = new McpServer({
name: "apidog",
version: "1.0.0",
description: "Apidog API testing tools for AI agents"
});
// Tool: run_test
server.tool(
"run_test",
{
projectId: z.string().describe("Apidog project ID"),
environmentId: z.string().optional().describe("Environment ID"),
testSuiteId: z.string().optional().describe("Test suite ID")
},
async ({ projectId, environmentId, testSuiteId }) => {
const apiKey = process.env.APIDOG_API_KEY;
if (!apiKey) {
return {
content: [{
type: "text",
text: "Error: APIDOG_API_KEY not set"
}]
};
}
let url = `https://api.apidog.com/v1/projects/${projectId}/tests/run`;
const params = new URLSearchParams();
if (environmentId) params.append("environmentId", environmentId);
if (testSuiteId) params.append("testSuiteId", testSuiteId);
if (params.toString()) url += `?${params.toString()}`;
try {
const response = await fetch(url, {
method: "POST",
headers: {
"Authorization": `Bearer ${apiKey}`,
"Content-Type": "application/json"
}
});
const results = await response.json();
return {
content: [{
type: "text",
text: JSON.stringify(results, null, 2)
}]
};
} catch (error) {
return {
content: [{
type: "text",
text: `Request failed: ${error instanceof Error ? error.message : String(error)}`
}]
};
}
}
);
// Tool: validate_schema
server.tool(
"validate_schema",
{
schema: z.object({}).describe("OpenAPI schema"),
strict: z.boolean().optional().default(false)
},
async ({ schema, strict }) => {
const apiKey = process.env.APIDOG_API_KEY;
if (!apiKey) {
return {
content: [{
type: "text",
text: "Error: APIDOG_API_KEY not set"
}]
};
}
const response = await fetch("https://api.apidog.com/v1/schemas/validate", {
method: "POST",
headers: {
"Authorization": `Bearer ${apiKey}`,
"Content-Type": "application/json"
},
body: JSON.stringify({ schema, strict })
});
const result = await response.json();
return {
content: [{
type: "text",
text: result.valid
? "Schema is valid"
: `Issues: ${JSON.stringify(result.errors || result.warnings, null, 2)}`
}]
};
}
);
// Tool: list_environments
server.tool(
"list_environments",
{
projectId: z.string().describe("Apidog project ID")
},
async ({ projectId }) => {
const apiKey = process.env.APIDOG_API_KEY;
if (!apiKey) {
return {
content: [{
type: "text",
text: "Error: APIDOG_API_KEY not set"
}]
};
}
const response = await fetch(
`https://api.apidog.com/v1/projects/${projectId}/environments`,
{
headers: { "Authorization": `Bearer ${apiKey}` }
}
);
const environments = await response.json();
return {
content: [{
type: "text",
text: environments.map((e: any) =>
`- ${e.name} (${e.id})${e.isDefault ? " [default]" : ""}`
).join("\n")
}]
};
}
);
const transport = new StdioServerTransport();
await server.connect(transport);
構築したもの
| コンポーネント | 目的 |
|---|---|
| MCPサーバー | AIエージェントとApidog APIを連携させる |
run_test |
テストコレクションをプログラムで実行する |
validate_schema |
デプロイ前にOpenAPIエラーを捕捉する |
list_environments |
利用可能なテスト環境を検出する |
| Zod検証 | 型安全なパラメータ処理 |
| 標準入出力トランスポート | Claude Code、Cursor、任意のMCPクライアントで動作する |
次のステップ
サーバーの拡張:
- 環境間のテスト結果を比較する
compare_responsesツールを追加する - 過去のテスト実行履歴を取得する
get_test_historyを追加する - モックエンドポイントを開始/停止する
trigger_mock_serverを追加する
本番環境での考慮事項:
- 不安定なネットワークリクエストのためのリトライロジックを追加する
- APIスロットリングを避けるためにレート制限を実装する
- 失敗したツール呼び出しのデバッグのためにログを追加する
- APIキーを環境変数の代わりにセキュアなボールトに保存する
チームとの共有:
- `@your-org/apidog-mcp-server`としてnpmに公開する
- 必要な環境変数を文書化する
- 一般的なクライアント向けのMCP設定例を含める
一般的な問題のトラブルシューティング
Claude CodeでMCPサーバーがロードされない:
~/.claude/settings.json内のパスが絶対パス(相対パスではない)であることを確認するnodeがPATHにあることを確認する:which node- ビルドされた
dist/index.jsが存在することを確認する:ls -la dist/ - Claude CodeのMCPログでエラーを探す
設定後にツールが表示されない:
- Claude Codeを完全に再起動する(終了して再度開く)
- TypeScriptがコンパイルされていることを確認するために
npm run buildを実行する server.connect()の前に3つすべてのツールが定義されていることを確認する- サーバーがエラーなしで起動することを確認する:
node dist/index.js
APIリクエストが401で失敗する:
APIDOG_API_KEYが設定で正しく設定されていることを確認する- キー値の周りに余分なスペースや引用符がないか確認する
- ApidogアカウントでAPIアクセスが有効になっていることを確認する
- キーを手動でテストする:
curl -H "Authorization: Bearer $APIDOG_API_KEY" https://api.apidog.com/v1/user
Zod検証エラー:
- パラメータ名がスキーマと正確に一致していることを確認する
- 必須フィールドが提供されていることを確認する(
projectIdの入力ミスがないか) - オプションフィールドがスキーマで
.optional()を使用していることを確認する - 完全なエラーメッセージを読み、Zodがどのフィールドで失敗したかを教えてくれる
TypeScriptコンパイルエラー:
- すべての依存関係がインストールされていることを確認するために
npm installを実行する - TypeScriptのバージョンを確認する:
npx tsc --version(5.xであること) - クリアして再ビルドする:
rm -rf dist && npm run build - フェッチ応答での型ミスマッチを探す(
as型アサーションを追加する)
MCPサーバーをローカルでテストする
本番環境にデプロイする前に、サーバーをローカルでテストします:
標準入出力による手動テスト:
# Start the server
node dist/index.js
# In another terminal, send a test message
echo '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}' | node dist/index.js
期待される出力:
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"tools": [
{ "name": "run_test", "description": "...", "inputSchema": {...} },
{ "name": "validate_schema", "description": "...", "inputSchema": {...} },
{ "name": "list_environments", "description": "...", "inputSchema": {...} }
]
}
}
ツール呼び出しをテストする:
echo '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"list_environments","arguments":{"projectId":"your-project-id"}}}' | node dist/index.js
これで、AIエージェントはApidogのテスト機能に直接アクセスできるようになりました。チャットとブラウザの間でのコピー&ペーストは不要です。手動でのテスト実行も不要です。コマンドを入力するだけで結果が返ってきます。
これこそがMCPの力です。AIエージェントをドメイン固有のツールで拡張し、彼らが本来の役割を果たすようにします。つまり、より迅速な出荷を支援します。
主要なポイント
- MCPサーバーはAIエージェントと外部APIを連携させる — 一度構築すれば、Claude Code、Cursor、および任意のMCP互換クライアントで利用可能
- 3つのツールがほとんどのAPIテストニーズをカバー — 実行には`run_test`、OpenAPI検証には`validate_schema`、検出には`list_environments`
- Zod検証により不正なパラメータを防止 — 型安全なツール定義によりAPI呼び出し前のエラーを捕捉
- 設定はツール固有 — Claude Codeは`~/.claude/settings.json`を使用し、Cursorは`.cursor/mcp.json`を使用
- 本番環境ではエラーハンドリングが必要 — デプロイ前にリトライロジック、レート制限、セキュアなキー保存を追加する
FAQ
AIにおけるMCPとは?MCP(モデルコンテキストプロトコル)は、AIエージェントが外部ツールやデータソースにアクセスできるようにする標準化されたプロトコルです。AIエージェント用のプラグインシステムと考えることができます。
Apidog用のMCPサーバーを作成するにはどうすればよいですか?`@modelcontextprotocol/sdk`をインストールし、Zod検証でツールを定義し、Apidog APIを呼び出すハンドラーを実装し、`StdioServerTransport`経由で接続します。
これをCursorで使用できますか?はい。プロジェクトルートの`.cursor/mcp.json`にMCPサーバー設定を追加してください。同じサーバーがClaude Code、Cursor、およびその他のMCPクライアントで動作します。
どのようなツールを公開すべきですか?まず、テストコレクション実行用の`run_test`、OpenAPI検証用の`validate_schema`、利用可能な環境取得用の`list_environments`から始めます。
Apidog MCPサーバーは本番環境に対応していますか?このチュートリアルコードは出発点です。本番環境で使用する前に、リトライロジック、レート制限、適切なエラーハンドリング、およびセキュアなAPIキー保存を追加してください。
Apidog APIキーは必要ですか?はい。`APIDOG_API_KEY`を環境変数として設定してください。サーバーは実行時にこれを読み取り、APIリクエストを認証します。
このMCPサーバーをチームと共有できますか?はい。プライベートパッケージとしてnpmに公開し、必要な環境変数を文書化し、MCP設定例を含めてください。
